command system; first for transport

This commit is contained in:
🪞👃🪞 2024-11-06 23:32:10 +01:00
parent 2e96b10fad
commit 524e283075
4 changed files with 230 additions and 46 deletions

View file

@ -0,0 +1,44 @@
use crate::*;
pub const fn key (code: KeyCode) -> KeyEvent {
KeyEvent {
code,
modifiers: KeyModifiers::NONE,
kind: crossterm::event::KeyEventKind::Press,
state: crossterm::event::KeyEventState::NONE
}
}
pub const fn ctrl (key: KeyEvent) -> KeyEvent {
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::CONTROL), ..key }
}
pub const fn alt (key: KeyEvent) -> KeyEvent {
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::ALT), ..key }
}
pub const fn shift (key: KeyEvent) -> KeyEvent {
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::SHIFT), ..key }
}
pub trait Command<T>: Sized {
fn run (&self, state: &mut T) -> Perhaps<Self>;
}
pub trait HandleKey<C: Command<Self> + 'static>: Sized {
const HANDLE_KEY_MAP: &'static [(KeyEvent, C)];
fn handle_key (&mut self, key: &KeyEvent) -> Perhaps<C> {
let mut run_command: Option<&'static C> = None;
for (binding, command) in Self::HANDLE_KEY_MAP.iter() {
if key == binding {
run_command = Some(command);
break
}
}
if let Some(command) = run_command {
command.run(self)
} else {
Ok(None)
}
}
}

View file

@ -38,6 +38,7 @@ use std::fmt::{Debug, Display};
submod! {
audio
color
command
edn
engine
focus

View file

@ -1,4 +1,26 @@
use crate::*;
enum SequencerCommand {
FocusNext,
FocusPrev,
FocusUp,
FocusDown,
FocusLeft,
FocusRight,
Transport(TransportCommand),
Phrase(PhrasePoolCommand),
Editor(PhraseEditorCommand),
}
enum PhrasePoolCommand {
}
enum PhraseLengthCommand {
}
enum PhraseEditorCommand {
}
/// Handle top-level events in standalone sequencer.
impl Handle<Tui> for Sequencer<Tui> {
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {

View file

@ -1,55 +1,172 @@
use crate::*;
impl Handle<Tui> for TransportToolbar<Tui> {
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
match from.event() {
key!(KeyCode::Left) => { self.focus.prev(); },
key!(KeyCode::Right) => { self.focus.next(); },
_ => match self.focus {
TransportToolbarFocus::PlayPause => return self.handle_play_pause(from),
TransportToolbarFocus::Bpm => return self.handle_bpm(from),
TransportToolbarFocus::Quant => return self.handle_quant(from),
TransportToolbarFocus::Sync => return self.handle_sync(from),
TransportToolbarFocus::Clock => {/*todo*/},
}
if let TuiEvent::Input(crossterm::event::Event::Key(event)) = from.event() {
let _undo = self.handle_key(event)?;
return Ok(Some(true))
}
Ok(Some(true))
Ok(None)
}
}
impl TransportToolbar<Tui> {
fn handle_play_pause (&mut self, from: &TuiInput) -> Perhaps<bool> {
match from.event() {
key!(KeyCode::Enter) => self.toggle_play().map(|_|())?,
_ => return Ok(None)
#[derive(Clone)]
pub enum TransportCommand {
FocusNext,
FocusPrev,
PlayFromStart,
TogglePlay,
Increment,
Decrement,
FineIncrement,
FineDecrement,
SeekUsec(f64),
SeekSample(f64),
SeekPulse(f64),
SetBpm(f64),
SetQuant(f64),
SetSync(f64),
}
impl HandleKey<TransportCommand> for TransportToolbar<Tui> {
const HANDLE_KEY_MAP: &'static [(KeyEvent, TransportCommand)] = &[
( key(KeyCode::Char(' ')), TransportCommand::FocusPrev),
(shift(key(KeyCode::Char(' '))), TransportCommand::FocusPrev),
( key(KeyCode::Left), TransportCommand::FocusPrev),
( key(KeyCode::Right), TransportCommand::FocusNext),
( key(KeyCode::Char('.')), TransportCommand::Increment),
( key(KeyCode::Char(',')), TransportCommand::Decrement),
( key(KeyCode::Char('>')), TransportCommand::FineIncrement),
( key(KeyCode::Char('<')), TransportCommand::FineDecrement),
];
}
impl<E: Engine> Command<TransportToolbar<E>> for TransportCommand {
fn run (&self, state: &mut TransportToolbar<E>) -> Perhaps<Self> {
match self {
Self::FocusNext => {
state.focus.next();
},
Self::FocusPrev => {
state.focus.prev();
},
Self::TogglePlay => {
state.toggle_play()?;
},
Self::Increment => {
match state.focus {
TransportToolbarFocus::Bpm => {
let bpm = state.clock.timebase().bpm.get();
return Self::SetBpm(bpm + 1.0).run(state)
},
TransportToolbarFocus::Quant => {
let quant = state.clock.quant.get() as usize;
let quant = next_note_length(quant) as f64;
return Self::SetQuant(quant).run(state)
},
TransportToolbarFocus::Sync => {
let sync = state.clock.sync.get() as usize;
let sync = next_note_length(sync) as f64;
return Self::SetSync(sync + 1.0).run(state)
},
TransportToolbarFocus::PlayPause => {
/*todo seek*/
},
TransportToolbarFocus::Clock => {
/*todo seek*/
},
}
},
Self::FineIncrement => {
match state.focus {
TransportToolbarFocus::Bpm => {
let bpm = state.clock.timebase().bpm.get();
return Self::SetBpm(bpm + 0.001).run(state)
},
TransportToolbarFocus::Quant => {
return Self::Increment.run(state)
},
TransportToolbarFocus::Sync => {
return Self::Increment.run(state)
},
TransportToolbarFocus::PlayPause => {
/*todo seek*/
},
TransportToolbarFocus::Clock => {
/*todo seek*/
},
}
},
Self::Decrement => {
match state.focus {
TransportToolbarFocus::Bpm => {
let bpm = state.clock.timebase().bpm.get();
return Self::SetBpm(bpm - 1.0).run(state)
},
TransportToolbarFocus::Quant => {
let quant = state.clock.quant.get() as usize;
let quant = prev_note_length(quant) as f64;
return Self::SetQuant(quant).run(state)
},
TransportToolbarFocus::Sync => {
let sync = state.clock.sync.get() as usize;
let sync = prev_note_length(sync) as f64;
return Self::SetSync(sync).run(state)
},
TransportToolbarFocus::PlayPause => {
/*todo seek*/
},
TransportToolbarFocus::Clock => {
/*todo seek*/
},
}
},
Self::FineDecrement => {
match state.focus {
TransportToolbarFocus::Bpm => {
let bpm = state.clock.timebase().bpm.get();
return Self::SetBpm(bpm - 0.001).run(state)
},
TransportToolbarFocus::Quant => {
return Self::Decrement.run(state)
},
TransportToolbarFocus::Sync => {
return Self::Decrement.run(state)
},
TransportToolbarFocus::PlayPause => {
/*todo seek*/
},
TransportToolbarFocus::Clock => {
/*todo seek*/
},
}
},
Self::SeekUsec(usec) => {
state.clock.current.update_from_usec(*usec);
},
Self::SeekSample(sample) => {
state.clock.current.update_from_sample(*sample);
},
Self::SeekPulse(pulse) => {
state.clock.current.update_from_pulse(*pulse);
},
Self::SetBpm(bpm) => {
let old_bpm = state.clock.timebase().bpm.get();
state.clock.timebase().bpm.set(*bpm);
return Ok(Some(Self::SetBpm(old_bpm)))
},
Self::SetQuant(quant) => {
let old_quant = state.clock.quant.get();
state.clock.quant.set(*quant);
return Ok(Some(Self::SetQuant(old_quant)))
},
Self::SetSync(sync) => {
let old_sync = state.clock.sync.get();
state.clock.sync.set(*sync);
return Ok(Some(Self::SetSync(old_sync)))
},
_ => { unimplemented!() }
}
Ok(Some(true))
}
fn handle_bpm (&mut self, from: &TuiInput) -> Perhaps<bool> {
let bpm = self.clock.timebase().bpm.get();
match from.event() {
key!(KeyCode::Char(',')) => { self.clock.timebase().bpm.set(bpm - 1.0); },
key!(KeyCode::Char('.')) => { self.clock.timebase().bpm.set(bpm + 1.0); },
key!(KeyCode::Char('<')) => { self.clock.timebase().bpm.set(bpm - 0.001); },
key!(KeyCode::Char('>')) => { self.clock.timebase().bpm.set(bpm + 0.001); },
_ => return Ok(None)
}
Ok(Some(true))
}
fn handle_quant (&mut self, from: &TuiInput) -> Perhaps<bool> {
let quant = self.clock.quant.get() as usize;
match from.event() {
key!(KeyCode::Char(',')) => { self.clock.quant.set(prev_note_length(quant) as f64); },
key!(KeyCode::Char('.')) => { self.clock.quant.set(next_note_length(quant) as f64); },
_ => return Ok(None)
}
return Ok(Some(true))
}
fn handle_sync (&mut self, from: &TuiInput) -> Perhaps<bool> {
let sync = self.clock.sync.get() as usize;
match from.event() {
key!(KeyCode::Char(',')) => { self.clock.quant.set(prev_note_length(sync) as f64); },
key!(KeyCode::Char('.')) => { self.clock.quant.set(next_note_length(sync) as f64); },
_ => return Ok(None)
}
return Ok(Some(true))
Ok(None)
}
}