mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
command system; first for transport
This commit is contained in:
parent
2e96b10fad
commit
524e283075
4 changed files with 230 additions and 46 deletions
44
crates/tek_core/src/command.rs
Normal file
44
crates/tek_core/src/command.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ use std::fmt::{Debug, Display};
|
|||
submod! {
|
||||
audio
|
||||
color
|
||||
command
|
||||
edn
|
||||
engine
|
||||
focus
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue