mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16: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! {
|
submod! {
|
||||||
audio
|
audio
|
||||||
color
|
color
|
||||||
|
command
|
||||||
edn
|
edn
|
||||||
engine
|
engine
|
||||||
focus
|
focus
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,26 @@
|
||||||
use crate::*;
|
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.
|
/// Handle top-level events in standalone sequencer.
|
||||||
impl Handle<Tui> for Sequencer<Tui> {
|
impl Handle<Tui> for Sequencer<Tui> {
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,172 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
impl Handle<Tui> for TransportToolbar<Tui> {
|
impl Handle<Tui> for TransportToolbar<Tui> {
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
match from.event() {
|
if let TuiEvent::Input(crossterm::event::Event::Key(event)) = from.event() {
|
||||||
key!(KeyCode::Left) => { self.focus.prev(); },
|
let _undo = self.handle_key(event)?;
|
||||||
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*/},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Some(true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
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))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
fn handle_sync (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
Ok(None)
|
||||||
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); },
|
#[derive(Clone)]
|
||||||
_ => return Ok(None)
|
pub enum TransportCommand {
|
||||||
}
|
FocusNext,
|
||||||
return Ok(Some(true))
|
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(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue