diff --git a/crates/tek_sequencer/src/arranger.rs b/crates/tek_sequencer/src/arranger.rs index 34c538c0..7133d6b6 100644 --- a/crates/tek_sequencer/src/arranger.rs +++ b/crates/tek_sequencer/src/arranger.rs @@ -3,17 +3,23 @@ use crate::*; /// Root level object for standalone `tek_arranger` pub struct Arranger { /// Which view is focused - pub focus_cursor: (usize, usize), + pub focus_cursor: (usize, usize), /// Controls the JACK transport. - pub transport: Option>>>, + pub transport: Option>>>, /// Contains all the sequencers. - pub arrangement: Arrangement, + pub arrangement: Arrangement, /// Pool of all phrases in the arrangement - pub phrases: Arc>>, + pub phrases: Arc>>, /// Phrase editor view - pub editor: PhraseEditor, + pub editor: PhraseEditor, /// This allows the sequencer view to be moved or hidden. - pub show_sequencer: Option, + pub show_sequencer: Option, + /// Status bar + pub status: ArrangerStatusBar, + /// Height of arrangement + pub arrangement_split: u16, + /// Width of phrase pool + pub phrases_split: u16, } /// Sections in the arranger app that may be focused #[derive(Copy, Clone, PartialEq, Eq)] @@ -27,6 +33,13 @@ pub enum ArrangerFocus { /// The phrase editor (sequencer) is focused PhraseEditor, } +/// Status bar for arranger ap +pub enum ArrangerStatusBar { + Transport, + Arrangement, + PhrasePool, + PhraseEditor, +} /// Represents the tracks and scenes of the composition. pub struct Arrangement { /// Name of arranger @@ -91,14 +104,30 @@ pub enum ArrangementViewMode { Vertical(usize), } /// Arrangement, rendered vertically (session/grid mode). -pub struct VerticalArranger<'a, E: Engine>( - pub &'a Arrangement, pub usize -); +pub struct VerticalArranger<'a, E: Engine>(pub &'a Arrangement, pub usize); /// Arrangement, rendered horizontally (arrangement/track mode). -pub struct HorizontalArranger<'a, E: Engine>( - pub &'a Arrangement -); +pub struct HorizontalArranger<'a, E: Engine>(pub &'a Arrangement); +/// General methods for arranger impl Arranger { + pub fn new ( + transport: Option>>>, + arrangement: Arrangement, + phrases: Arc>>, + ) -> Self { + let mut app = Self { + focus_cursor: (0, 1), + show_sequencer: Some(tek_core::Direction::Down), + editor: PhraseEditor::new(), + status: ArrangerStatusBar::Transport, + transport, + arrangement, + phrases, + phrases_split: 20, + arrangement_split: 20, + }; + app.update_focus(); + app + } /// Toggle global play/pause pub fn toggle_play (&mut self) -> Perhaps { match self.transport { diff --git a/crates/tek_sequencer/src/arranger_cli.rs b/crates/tek_sequencer/src/arranger_cli.rs index e9cf03c2..7e883bd6 100644 --- a/crates/tek_sequencer/src/arranger_cli.rs +++ b/crates/tek_sequencer/src/arranger_cli.rs @@ -51,16 +51,11 @@ impl ArrangerCli { } )? ); - let mut app = Arranger { - focus_cursor: (0, 1), - transport: self.transport.then_some(transport), - show_sequencer: Some(tek_core::Direction::Down), - editor: PhraseEditor::new(), + Tui::run(Arc::new(RwLock::new(Arranger::new( + self.transport.then_some(transport), arrangement, phrases, - }; - app.update_focus(); - Tui::run(Arc::new(RwLock::new(app)))?; + ))))?; Ok(()) } } diff --git a/crates/tek_sequencer/src/arranger_cmd.rs b/crates/tek_sequencer/src/arranger_cmd.rs index 58514bab..402633c6 100644 --- a/crates/tek_sequencer/src/arranger_cmd.rs +++ b/crates/tek_sequencer/src/arranger_cmd.rs @@ -4,16 +4,16 @@ impl Handle for Arranger { fn handle (&mut self, from: &TuiInput) -> Perhaps { if !self.handle_focused(from)?.unwrap_or(false) { match from.event() { - key!(KeyCode::Tab) => { self.focus_next(); }, - key!(Shift-KeyCode::Tab) => { self.focus_prev(); }, - key!(KeyCode::BackTab) => { self.focus_prev(); }, - key!(Shift-KeyCode::BackTab) => { self.focus_prev(); }, - key!(KeyCode::Up) => { self.focus_up(); }, - key!(KeyCode::Down) => { self.focus_down(); }, - key!(KeyCode::Left) => { self.focus_left(); }, - key!(KeyCode::Right) => { self.focus_right(); }, - key!(KeyCode::Char('e')) => { self.edit_phrase(); }, - key!(KeyCode::Char(' ')) => { self.toggle_play()?; }, + key!(KeyCode::Tab) => { self.focus_next(); }, + key!(Shift-KeyCode::Tab) => { self.focus_prev(); }, + key!(KeyCode::BackTab) => { self.focus_prev(); }, + key!(Shift-KeyCode::BackTab) => { self.focus_prev(); }, + key!(KeyCode::Up) => { self.focus_up(); }, + key!(KeyCode::Down) => { self.focus_down(); }, + key!(KeyCode::Left) => { self.focus_left(); }, + key!(KeyCode::Right) => { self.focus_right(); }, + key!(KeyCode::Char('e')) => { self.edit_phrase(); }, + key!(KeyCode::Char(' ')) => { self.toggle_play()?; }, key!(KeyCode::Char('n')) => { self.rename_selected(); }, _ => return Ok(None) } @@ -26,11 +26,24 @@ impl Arranger { fn handle_focused (&mut self, from: &TuiInput) -> Perhaps { match self.focused() { ArrangerFocus::Transport => self.transport.handle(from), - ArrangerFocus::PhrasePool => self.phrases.handle(from), - ArrangerFocus::PhraseEditor => self.editor.handle(from), ArrangerFocus::Arrangement => self.handle_arrangement(from), + ArrangerFocus::PhrasePool => self.handle_pool(from), + ArrangerFocus::PhraseEditor => self.editor.handle(from), } } + /// Helper for phrase event passthru when phrase pool is focused + fn handle_pool (&mut self, from: &TuiInput) -> Perhaps { + match from.event() { + key!(KeyCode::Char('<')) => { + self.phrases_split = self.phrases_split.saturating_sub(1).max(12); + }, + key!(KeyCode::Char('>')) => { + self.phrases_split = self.phrases_split + 1; + }, + _ => return self.arrangement.handle(from) + } + Ok(Some(true)) + } /// Helper for phrase event passthru when arrangement is focused fn handle_arrangement (&mut self, from: &TuiInput) -> Perhaps { let mut handle_phrase = ||{ diff --git a/crates/tek_sequencer/src/arranger_tui.rs b/crates/tek_sequencer/src/arranger_tui.rs index bb07c5d9..8e00f1d5 100644 --- a/crates/tek_sequencer/src/arranger_tui.rs +++ b/crates/tek_sequencer/src/arranger_tui.rs @@ -7,10 +7,10 @@ impl Content for Arranger { add(&self.transport)?; let arrangement = &self.arrangement as &dyn Widget; if let Some(direction) = self.show_sequencer { - add(&arrangement.split(direction, 20, - self.phrases.clone().split(direction.ccw(), 20, + add(&arrangement.split(direction, self.arrangement_split, + self.phrases.clone().split(direction.ccw(), self.phrases_split, &self.editor as &dyn Widget - ).min_y(20) + ).min_y(self.arrangement_split) ).fill_y()) } else { add(&self.arrangement) diff --git a/crates/tek_sequencer/src/sequencer.rs b/crates/tek_sequencer/src/sequencer.rs index b2896acc..b241a284 100644 --- a/crates/tek_sequencer/src/sequencer.rs +++ b/crates/tek_sequencer/src/sequencer.rs @@ -18,7 +18,20 @@ pub struct Sequencer { } /// Sections in the sequencer app that may be focused #[derive(Copy, Clone, PartialEq, Eq)] -pub enum SequencerFocus { Transport, PhrasePool, PhraseEditor } +pub enum SequencerFocus { + /// The transport (toolbar) is focused + Transport, + /// The phrase list (pool) is focused + PhrasePool, + /// The phrase editor (sequencer) is focused + PhraseEditor, +} +/// Status bar for sequencer app +pub enum SequencerStatusBar { + Transport, + PhrasePool, + PhraseEditor, +} /// Contains all phrases in a project pub struct PhrasePool { _engine: PhantomData, @@ -35,7 +48,9 @@ pub struct PhrasePool { } /// Modes for phrase pool pub enum PhrasePoolMode { + /// Renaming a pattern Rename(usize, String), + /// Editing the length of a pattern Length(usize, usize, PhraseLengthFocus), } /// A MIDI sequence. @@ -328,12 +343,17 @@ impl PhrasePlayer { self.overdub = !self.overdub; } } +/// Displays and edits phrase length pub struct PhraseLength { _engine: PhantomData, - pub ppq: usize, - pub bpb: usize, + /// Pulses per beat (quaver) + pub ppq: usize, + /// Beats per bar + pub bpb: usize, + /// Length of phrase in pulses pub pulses: usize, - pub focus: Option, + /// Selected subdivision + pub focus: Option, } impl PhraseLength { pub fn new (pulses: usize, focus: Option) -> Self { @@ -365,7 +385,14 @@ impl PhraseLength { } } #[derive(Copy,Clone)] -pub enum PhraseLengthFocus { Bar, Beat, Tick } +pub enum PhraseLengthFocus { + /// Editing the number of bars + Bar, + /// Editing the number of beats + Beat, + /// Editing the number of ticks + Tick, +} impl PhraseLengthFocus { pub fn next (&mut self) { *self = match self {