From e95230a34068765f60a4b570b4a2a6179ff32822 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 19 Nov 2024 00:30:03 +0100 Subject: [PATCH] wip: p.58, e=57 --- crates/tek_tui/src/tui_apis.rs | 5 +- crates/tek_tui/src/tui_command.rs | 124 ++++++++++++++---------- crates/tek_tui/src/tui_input.rs | 6 +- crates/tek_tui/src/tui_model.rs | 59 ++++++++++++ crates/tek_tui/src/tui_view.rs | 153 +++++++----------------------- 5 files changed, 173 insertions(+), 174 deletions(-) diff --git a/crates/tek_tui/src/tui_apis.rs b/crates/tek_tui/src/tui_apis.rs index 4a6d2b29..ae177ea2 100644 --- a/crates/tek_tui/src/tui_apis.rs +++ b/crates/tek_tui/src/tui_apis.rs @@ -25,8 +25,9 @@ impl PhraseTui { entered: false, mode: false, now: Arc::new(0.into()), - width: 0.into(), - height: 0.into(), + size: Measure::default(), + //width: 0.into(), + //height: 0.into(), note_axis: RwLock::new(FixedAxis { start: 12, point: Some(36), diff --git a/crates/tek_tui/src/tui_command.rs b/crates/tek_tui/src/tui_command.rs index 93003764..279397b6 100644 --- a/crates/tek_tui/src/tui_command.rs +++ b/crates/tek_tui/src/tui_command.rs @@ -38,17 +38,21 @@ pub enum SequencerCommand { impl Command for SequencerCommand where - T: FocusGrid + PhrasesControl + PhraseControl + ClockApi + PlayheadApi + T: PhrasesControl + PhraseControl + ClockApi + PlayheadApi + + FocusGrid + FocusEnter { - fn execute (self, state: T) -> Perhaps { + fn execute (self, state: &mut T) -> Perhaps { use SequencerCommand::*; - match self { - Focus(cmd) => delegate(cmd, Focus, state), - Phrases(cmd) => delegate(cmd, Phrases, state), - Editor(cmd) => delegate(cmd, Editor, state), - Clock(cmd) => delegate(cmd, Clock, state), - Playhead(cmd) => delegate(cmd, Playhead, state) - } + Ok(match self { + Focus(cmd) => cmd.execute(state)?.map(Focus), + Phrases(cmd) => cmd.execute(state)?.map(Phrases), + Editor(cmd) => cmd.execute(state)?.map(Editor), + Clock(cmd) => cmd.execute(state)?.map(Clock), + Playhead(cmd) => cmd.execute(state)?.map(Playhead), + Undo => { todo!() }, + Redo => { todo!() }, + Clear => { todo!() }, + }) } } @@ -72,28 +76,32 @@ pub enum ArrangerCommand { impl Command for ArrangerCommand where - T: FocusGrid + ArrangerControl + HasPhrases + PhraseControl + ClockApi + PlayheadApi + T: ArrangerControl + HasPhrases + PhraseControl + ClockApi + PlayheadApi + + FocusGrid + FocusEnter { fn execute (self, state: &mut T) -> Perhaps { use ArrangerCommand::*; - match self { - Focus(cmd) => { delegate(cmd, Focus, &mut state) }, - Scene(cmd) => { delegate(cmd, Scene, &mut state) }, - Track(cmd) => { delegate(cmd, Track, &mut state) }, - Clip(cmd) => { delegate(cmd, Clip, &mut state) }, - Phrases(cmd) => { delegate(cmd, Phrases, &mut state) }, - Editor(cmd) => { delegate(cmd, Editor, &mut state) }, - Clock(cmd) => { delegate(cmd, Clock, &mut state) }, - Playhead(cmd) => { delegate(cmd, Playhead, &mut state) }, + Ok(match self { + Focus(cmd) => cmd.execute(state)?.map(Focus), + Scene(cmd) => cmd.execute(state)?.map(Scene), + Track(cmd) => cmd.execute(state)?.map(Track), + Clip(cmd) => cmd.execute(state)?.map(Clip), + Phrases(cmd) => cmd.execute(state)?.map(Phrases), + Editor(cmd) => cmd.execute(state)?.map(Editor), + Clock(cmd) => cmd.execute(state)?.map(Clock), + Playhead(cmd) => cmd.execute(state)?.map(Playhead), Zoom(zoom) => { todo!(); }, - Select(selected) => { state.selected = selected; Ok(None) }, + Select(selected) => { + state.selected = selected; + None + }, EditPhrase(phrase) => { state.editor.phrase = phrase.clone(); state.focus(ArrangerFocus::PhraseEditor); state.focus_enter(); - Ok(None) + None } - } + }) } } @@ -124,31 +132,21 @@ pub enum PhrasesCommand { Phrase(PhrasePoolCommand), Rename(PhraseRenameCommand), Length(PhraseLengthCommand), - MoveUp, - MoveDown, } impl Command for PhrasesCommand { fn execute (self, state: &mut T) -> Perhaps { use PhraseRenameCommand as Rename; use PhraseLengthCommand as Length; - match self { + Ok(match self { + Self::Phrase(command) => command.execute(state)?.map(Self::Phrase), + Self::Rename(command) => command.execute(state)?.map(Self::Rename), + Self::Length(command) => command.execute(state)?.map(Self::Length), Self::Select(phrase) => { - state.phrase = phrase + state.phrase = phrase; + None }, - Self::Phrase(command) => { - return Ok(command.execute(&mut state)?.map(Self::Phrase)) - } - Self::Rename(command) => match command { - Rename::Begin => self.phrases_rename_begin(), - _ => return Ok(command.execute(state)?.map(Self::Rename)), - }, - Self::Length(command) => match command { - Length::Begin => self.phrases_length_begin(), - _ => return Ok(command.execute(state)?.map(Self::Length)), - }, - } - Ok(None) + }) } } @@ -199,7 +197,7 @@ impl Command for PhraseLengthCommand { } Ok(None) } else if self == Begin { - self.phrases_length_begin(); + state.phrase_length_begin(); Ok(None) } else { unreachable!() @@ -239,7 +237,7 @@ where }; Ok(None) } else if self == Begin { - self.phrase_rename_begin(); + state.phrase_rename_begin(); Ok(None) } else { unreachable!() @@ -279,81 +277,107 @@ where //} fn execute (self, state: &mut T) -> Perhaps { use PhraseCommand::*; - match self.translate(state) { + Ok(match self { ToggleDirection => { state.mode = !state.mode; + None }, EnterEditMode => { state.focus_enter(); + None }, ExitEditMode => { state.focus_exit(); + None }, TimeZoomOut => { let scale = state.time_axis().read().unwrap().scale; - state.time_axis().write().unwrap().scale = next_note_length(scale) + state.time_axis().write().unwrap().scale = next_note_length(scale); + None }, TimeZoomIn => { let scale = state.time_axis().read().unwrap().scale; - state.time_axis().write().unwrap().scale = prev_note_length(scale) + state.time_axis().write().unwrap().scale = prev_note_length(scale); + None }, TimeCursorDec => { let scale = state.time_axis().read().unwrap().scale; state.time_axis().write().unwrap().point_dec(scale); + None }, TimeCursorInc => { let scale = state.time_axis().read().unwrap().scale; state.time_axis().write().unwrap().point_inc(scale); + None }, TimeScrollDec => { let scale = state.time_axis().read().unwrap().scale; state.time_axis().write().unwrap().start_dec(scale); + None }, TimeScrollInc => { let scale = state.time_axis().read().unwrap().scale; state.time_axis().write().unwrap().start_inc(scale); + None }, NoteCursorDec => { let mut axis = state.note_axis().write().unwrap(); axis.point_inc(1); - if let Some(point) = axis.point { if point > 73 { axis.point = Some(73); } } + if let Some(point) = axis.point { + if point > 73 { axis.point = Some(73); } + } + None }, NoteCursorInc => { let mut axis = state.note_axis().write().unwrap(); axis.point_dec(1); - if let Some(point) = axis.point { if point < axis.start { axis.start = (point / 2) * 2; } } + if let Some(point) = axis.point { + if point < axis.start { axis.start = (point / 2) * 2; } + } + None }, NoteScrollDec => { state.note_axis().write().unwrap().start_inc(1); + None }, NoteScrollInc => { state.note_axis().write().unwrap().start_dec(1); + None }, NoteLengthDec => { - *state.note_len_mut() = prev_note_length(state.note_len()) + *state.note_len_mut() = prev_note_length(state.note_len()); + None }, NoteLengthInc => { - *state.note_len_mut() = next_note_length(state.note_len()) + *state.note_len_mut() = next_note_length(state.note_len()); + None }, NotePageUp => { let mut axis = state.note_axis().write().unwrap(); axis.start_dec(3); axis.point_dec(3); + None }, NotePageDown => { let mut axis = state.note_axis().write().unwrap(); axis.start_inc(3); axis.point_inc(3); + None }, NoteAppend => if state.focus_entered() { state.put(); state.time_cursor_advance(); + None + } else { + None }, NoteSet => if state.focus_entered() { state.put(); + None + } else { + None }, _ => unreachable!() - } - Ok(None) + }) } } diff --git a/crates/tek_tui/src/tui_input.rs b/crates/tek_tui/src/tui_input.rs index f6c762fe..0b8ea811 100644 --- a/crates/tek_tui/src/tui_input.rs +++ b/crates/tek_tui/src/tui_input.rs @@ -52,7 +52,8 @@ where impl InputToCommand for SequencerCommand where - T: SequencerControl + TransportControl + PhrasesControl + PhraseControl + HasFocus + T: SequencerControl + TransportControl + PhrasesControl + PhraseControl + PlayheadApi + + HasFocus + FocusGrid { fn input_to_command (state: &T, input: &TuiInput) -> Option { use FocusCommand::*; @@ -92,7 +93,8 @@ where impl InputToCommand for ArrangerCommand where - T: ArrangerControl + TransportControl + PhrasesControl + PhraseControl + HasFocus + T: ArrangerControl + TransportControl + PhrasesControl + PhraseControl + PlayheadApi + + HasFocus + FocusGrid { fn input_to_command (state: &T, input: &TuiInput) -> Option { use FocusCommand::*; diff --git a/crates/tek_tui/src/tui_model.rs b/crates/tek_tui/src/tui_model.rs index e5ff416c..aa64fe1e 100644 --- a/crates/tek_tui/src/tui_model.rs +++ b/crates/tek_tui/src/tui_model.rs @@ -176,6 +176,29 @@ impl HasScenes for ArrangerTui { } } +/// Display mode of arranger +#[derive(Clone, PartialEq)] +pub enum ArrangerMode { + /// Tracks are rows + Horizontal, + /// Tracks are columns + Vertical(usize), +} + +/// Arranger display mode can be cycled +impl ArrangerMode { + /// Cycle arranger display mode + pub fn to_next (&mut self) { + *self = match self { + Self::Horizontal => Self::Vertical(1), + Self::Vertical(1) => Self::Vertical(2), + Self::Vertical(2) => Self::Vertical(2), + Self::Vertical(0) => Self::Horizontal, + Self::Vertical(_) => Self::Vertical(0), + } + } +} + #[derive(Default, Debug, Clone)] pub struct ArrangerScene { /// Name of scene @@ -425,6 +448,42 @@ pub enum PhrasesMode { Length(usize, usize, PhraseLengthFocus), } +/// Displays and edits phrase length. +pub struct PhraseLength { + /// Pulses per beat (quaver) + pub ppq: usize, + /// Beats per bar + pub bpb: usize, + /// Length of phrase in pulses + pub pulses: usize, + /// Selected subdivision + pub focus: Option, +} + +impl PhraseLength { + pub fn new (pulses: usize, focus: Option) -> Self { + Self { ppq: PPQ, bpb: 4, pulses, focus } + } + pub fn bars (&self) -> usize { + self.pulses / (self.bpb * self.ppq) + } + pub fn beats (&self) -> usize { + (self.pulses % (self.bpb * self.ppq)) / self.ppq + } + pub fn ticks (&self) -> usize { + self.pulses % self.ppq + } + pub fn bars_string (&self) -> String { + format!("{}", self.bars()) + } + pub fn beats_string (&self) -> String { + format!("{}", self.beats()) + } + pub fn ticks_string (&self) -> String { + format!("{:>02}", self.ticks()) + } +} + /// Contains state for viewing and editing a phrase pub struct PhraseTui { /// Phrase being played diff --git a/crates/tek_tui/src/tui_view.rs b/crates/tek_tui/src/tui_view.rs index 077b01f7..d7767254 100644 --- a/crates/tek_tui/src/tui_view.rs +++ b/crates/tek_tui/src/tui_view.rs @@ -2,26 +2,10 @@ use crate::*; pub struct TransportView<'a, T: TransportViewState>(pub &'a T); -pub trait TransportViewState: Send + Sync { +pub trait TransportViewState: ClockApi + PlayheadApi + Send + Sync { fn transport_selected (&self) -> TransportFocus; fn transport_focused (&self) -> bool; fn transport_state (&self) -> Option; - fn bpm_value (&self) -> f64; - fn sync_value (&self) -> f64; - fn format_beat (&self) -> String; - fn format_msu (&self) -> String; -} - -impl TransportViewState for TransportTui { - fn transport_selected (&self) -> TransportFocus { - self.focus - } - fn transport_focused (&self) -> bool { - true - } - fn transport_state (&self) -> Option { - *self.playing().read().unwrap() - } fn bpm_value (&self) -> f64 { self.bpm().get() } @@ -36,6 +20,18 @@ impl TransportViewState for TransportTui { } } +impl TransportViewState for TransportTui { + fn transport_selected (&self) -> TransportFocus { + self.focus + } + fn transport_focused (&self) -> bool { + true + } + fn transport_state (&self) -> Option { + *self.playing().read().unwrap() + } +} + impl TransportViewState for SequencerTui { fn transport_selected (&self) -> TransportFocus { self.focus @@ -46,18 +42,6 @@ impl TransportViewState for SequencerTui { fn transport_state (&self) -> Option { *self.playing().read().unwrap() } - fn bpm_value (&self) -> f64 { - self.bpm().get() - } - fn sync_value (&self) -> f64 { - self.sync().get() - } - fn format_beat (&self) -> String { - self.current().format_beat() - } - fn format_msu (&self) -> String { - self.current().usec.format_msu() - } } impl TransportViewState for ArrangerTui { @@ -70,18 +54,6 @@ impl TransportViewState for ArrangerTui { fn transport_state (&self) -> Option { *self.playing().read().unwrap() } - fn bpm_value (&self) -> f64 { - self.bpm().get() - } - fn sync_value (&self) -> f64 { - self.sync().get() - } - fn format_beat (&self) -> String { - self.current().format_beat() - } - fn format_msu (&self) -> String { - self.current().usec.format_msu() - } } pub trait ArrangerViewState { @@ -161,31 +133,34 @@ impl PhrasesViewState for ArrangerTui { pub struct PhraseView<'a, T: PhraseViewState>(pub &'a T); pub trait PhraseViewState: Send + Sync { + fn phrase (&self) -> &Option>>; fn phrase_focused (&self) -> bool; + fn phrase_editor_size (&self) -> &Measure; fn entered (&self) -> bool; fn keys (&self) -> &Buffer; - fn phrase (&self) -> &Option>>; fn buffer (&self) -> &BigBuffer; fn note_len (&self) -> usize; fn note_axis (&self) -> &RwLock>; fn time_axis (&self) -> &RwLock>; - fn size (&self) -> &Measure; fn now (&self) -> &Arc; } impl PhraseViewState for PhraseTui { + fn phrase (&self) -> &Option>> { + &self.phrase + } fn phrase_focused (&self) -> bool { self.focused } + fn phrase_editor_size (&self) -> &Measure { + todo!() + } fn entered (&self) -> bool { self.entered } fn keys (&self) -> &Buffer { &self.keys } - fn phrase (&self) -> &Option>> { - &self.phrase - } fn buffer (&self) -> &BigBuffer { &self.buffer } @@ -198,27 +173,27 @@ impl PhraseViewState for PhraseTui { fn time_axis (&self) -> &RwLock> { &self.time_axis } - fn size (&self) -> &Measure { - &self.size - } fn now (&self) -> &Arc { &self.now } } impl PhraseViewState for SequencerTui { + fn phrase (&self) -> &Option>> { + todo!() + } fn phrase_focused (&self) -> bool { todo!() } + fn phrase_editor_size (&self) -> &Measure { + todo!() + } fn entered (&self) -> bool { todo!() } fn keys (&self) -> &Buffer { todo!() } - fn phrase (&self) -> &Option>> { - todo!() - } fn buffer (&self) -> &BigBuffer { todo!() } @@ -231,27 +206,27 @@ impl PhraseViewState for SequencerTui { fn time_axis (&self) -> &RwLock> { todo!() } - fn size (&self) -> &Measure { - todo!() - } fn now (&self) -> &Arc { todo!() } } impl PhraseViewState for ArrangerTui { + fn phrase (&self) -> &Option>> { + todo!() + } fn phrase_focused (&self) -> bool { todo!() } + fn phrase_editor_size (&self) -> &Measure { + todo!() + } fn entered (&self) -> bool { todo!() } fn keys (&self) -> &Buffer { todo!() } - fn phrase (&self) -> &Option>> { - todo!() - } fn buffer (&self) -> &BigBuffer { todo!() } @@ -264,73 +239,11 @@ impl PhraseViewState for ArrangerTui { fn time_axis (&self) -> &RwLock> { todo!() } - fn size (&self) -> &Measure { - todo!() - } fn now (&self) -> &Arc { todo!() } } -/// Displays and edits phrase length. -pub struct PhraseLength { - /// Pulses per beat (quaver) - pub ppq: usize, - /// Beats per bar - pub bpb: usize, - /// Length of phrase in pulses - pub pulses: usize, - /// Selected subdivision - pub focus: Option, -} - -impl PhraseLength { - pub fn new (pulses: usize, focus: Option) -> Self { - Self { ppq: PPQ, bpb: 4, pulses, focus } - } - pub fn bars (&self) -> usize { - self.pulses / (self.bpb * self.ppq) - } - pub fn beats (&self) -> usize { - (self.pulses % (self.bpb * self.ppq)) / self.ppq - } - pub fn ticks (&self) -> usize { - self.pulses % self.ppq - } - pub fn bars_string (&self) -> String { - format!("{}", self.bars()) - } - pub fn beats_string (&self) -> String { - format!("{}", self.beats()) - } - pub fn ticks_string (&self) -> String { - format!("{:>02}", self.ticks()) - } -} - -/// Display mode of arranger -#[derive(Clone, PartialEq)] -pub enum ArrangerMode { - /// Tracks are rows - Horizontal, - /// Tracks are columns - Vertical(usize), -} - -/// Arranger display mode can be cycled -impl ArrangerMode { - /// Cycle arranger display mode - pub fn to_next (&mut self) { - *self = match self { - Self::Horizontal => Self::Vertical(1), - Self::Vertical(1) => Self::Vertical(2), - Self::Vertical(2) => Self::Vertical(2), - Self::Vertical(0) => Self::Horizontal, - Self::Vertical(_) => Self::Vertical(0), - } - } -} - fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> { let mut widths = vec![]; let mut total = 0;