mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
354 lines
12 KiB
Rust
354 lines
12 KiB
Rust
use crate::*;
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum TransportCommand {
|
|
Focus(FocusCommand),
|
|
Clock(ClockCommand),
|
|
Playhead(PlayheadCommand),
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum SequencerCommand {
|
|
Focus(FocusCommand),
|
|
Undo,
|
|
Redo,
|
|
Clear,
|
|
Clock(ClockCommand),
|
|
Playhead(PlayheadCommand),
|
|
Phrases(PhrasesCommand),
|
|
Editor(PhraseCommand),
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum ArrangerCommand {
|
|
Focus(FocusCommand),
|
|
Undo,
|
|
Redo,
|
|
Clear,
|
|
Clock(ClockCommand),
|
|
Playhead(PlayheadCommand),
|
|
Scene(ArrangerSceneCommand),
|
|
Track(ArrangerTrackCommand),
|
|
Clip(ArrangerClipCommand),
|
|
Select(ArrangerSelection),
|
|
Zoom(usize),
|
|
Phrases(PhrasePoolCommand),
|
|
Editor(PhraseCommand),
|
|
EditPhrase(Option<Arc<RwLock<Phrase>>>),
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Debug)]
|
|
pub enum PhrasesCommand {
|
|
Select(usize),
|
|
Edit(PhrasePoolCommand),
|
|
Rename(PhraseRenameCommand),
|
|
Length(PhraseLengthCommand),
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum PhraseRenameCommand {
|
|
Begin,
|
|
Set(String),
|
|
Confirm,
|
|
Cancel,
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
pub enum PhraseLengthCommand {
|
|
Begin,
|
|
Next,
|
|
Prev,
|
|
Inc,
|
|
Dec,
|
|
Set(usize),
|
|
Cancel,
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Debug)]
|
|
pub enum PhraseCommand {
|
|
// TODO: 1-9 seek markers that by default start every 8th of the phrase
|
|
ToggleDirection,
|
|
EnterEditMode,
|
|
ExitEditMode,
|
|
NoteAppend,
|
|
NoteSet,
|
|
NoteCursorSet(usize),
|
|
NoteLengthSet(usize),
|
|
NoteScrollSet(usize),
|
|
TimeCursorSet(usize),
|
|
TimeScrollSet(usize),
|
|
TimeZoomSet(usize),
|
|
}
|
|
|
|
impl<T: TransportControl> Command<T> for TransportCommand {
|
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
|
use TransportCommand::{Focus, Clock, Playhead};
|
|
use FocusCommand::{Next, Prev};
|
|
use ClockCommand::{SetBpm, SetQuant, SetSync};
|
|
Ok(Some(match self {
|
|
Focus(Next) => { todo!() }
|
|
Focus(Prev) => { todo!() },
|
|
Focus(_) => { unimplemented!() },
|
|
Clock(SetBpm(bpm)) => Clock(SetBpm(state.bpm().set(bpm))),
|
|
Clock(SetQuant(quant)) => Clock(SetQuant(state.quant().set(quant))),
|
|
Clock(SetSync(sync)) => Clock(SetSync(state.sync().set(sync))),
|
|
_ => return Ok(None)
|
|
}))
|
|
}
|
|
}
|
|
|
|
impl<T> Command<T> for SequencerCommand
|
|
where
|
|
T: FocusGrid + PhrasesControl + PhraseControl + ClockApi + PlayheadApi
|
|
{
|
|
fn execute (self, state: T) -> Perhaps<Self> {
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> Command<T> for ArrangerCommand
|
|
where
|
|
T: FocusGrid + ArrangerControl + HasPhrases + PhraseControl + ClockApi + PlayheadApi
|
|
{
|
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
|
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) },
|
|
Zoom(zoom) => { todo!(); },
|
|
Select(selected) => { state.selected = selected; Ok(None) },
|
|
EditPhrase(phrase) => {
|
|
state.editor.phrase = phrase.clone();
|
|
state.focus(ArrangerFocus::PhraseEditor);
|
|
state.focus_enter();
|
|
Ok(None)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: ArrangerControl> Command<T> for ArrangerSceneCommand {
|
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
|
todo!();
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
impl<T: ArrangerControl> Command<T> for ArrangerTrackCommand {
|
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
|
todo!();
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
impl<T: ArrangerControl> Command<T> for ArrangerClipCommand {
|
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
|
todo!();
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
impl<T: PhrasesControl> Command<T> for PhrasesCommand {
|
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
|
use PhraseRenameCommand as Rename;
|
|
use PhraseLengthCommand as Length;
|
|
match self {
|
|
Self::Select(phrase) => {
|
|
state.phrase = phrase
|
|
},
|
|
Self::Edit(command) => {
|
|
return Ok(command.execute(&mut state)?.map(Self::Edit))
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
|
|
impl<T: PhrasesControl> Command<T> for PhraseLengthCommand {
|
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
|
use PhraseLengthFocus::*;
|
|
use PhraseLengthCommand::*;
|
|
if let Some(PhrasesMode::Length(phrase, ref mut length, ref mut focus)) = state.mode {
|
|
match self {
|
|
Self::Cancel => {
|
|
state.mode = None;
|
|
},
|
|
Self::Prev => {
|
|
focus.prev()
|
|
},
|
|
Self::Next => {
|
|
focus.next()
|
|
},
|
|
Self::Inc => match focus {
|
|
Bar => { *length += 4 * PPQ },
|
|
Beat => { *length += PPQ },
|
|
Tick => { *length += 1 },
|
|
},
|
|
Self::Dec => match focus {
|
|
Bar => { *length = length.saturating_sub(4 * PPQ) },
|
|
Beat => { *length = length.saturating_sub(PPQ) },
|
|
Tick => { *length = length.saturating_sub(1) },
|
|
},
|
|
Self::Set(length) => {
|
|
let mut phrase = state.phrases[phrase].write().unwrap();
|
|
let old_length = phrase.length;
|
|
phrase.length = length;
|
|
state.mode = None;
|
|
return Ok(Some(Self::Set(old_length)))
|
|
},
|
|
_ => unreachable!()
|
|
}
|
|
Ok(None)
|
|
} else if self == Begin {
|
|
self.phrases_length_begin();
|
|
Ok(None)
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: PhrasesControl> Command<T> for PhraseRenameCommand {
|
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
|
use PhraseRenameCommand::*;
|
|
if let Some(PhrasesMode::Rename(phrase, ref mut old_name)) = state.mode {
|
|
match self {
|
|
Set(s) => {
|
|
state.phrases[phrase].write().unwrap().name = s.into();
|
|
return Ok(Some(Self::Set(old_name.clone())))
|
|
},
|
|
Confirm => {
|
|
let old_name = old_name.clone();
|
|
state.mode = None;
|
|
return Ok(Some(Self::Set(old_name)))
|
|
},
|
|
Cancel => {
|
|
let mut phrase = state.phrases[phrase].write().unwrap();
|
|
phrase.name = old_name.clone();
|
|
},
|
|
_ => unreachable!()
|
|
};
|
|
Ok(None)
|
|
} else if self == Begin {
|
|
self.phrases_rename_begin();
|
|
Ok(None)
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: PhraseControl> Command<T> for PhraseCommand {
|
|
//fn translate (self, state: &PhraseTui<E>) -> Self {
|
|
//use PhraseCommand::*;
|
|
//match self {
|
|
//GoUp => match state.entered { true => NoteCursorInc, false => NoteScrollInc, },
|
|
//GoDown => match state.entered { true => NoteCursorDec, false => NoteScrollDec, },
|
|
//GoLeft => match state.entered { true => TimeCursorDec, false => TimeScrollDec, },
|
|
//GoRight => match state.entered { true => TimeCursorInc, false => TimeScrollInc, },
|
|
//_ => self
|
|
//}
|
|
//}
|
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
|
use PhraseCommand::*;
|
|
match self.translate(state) {
|
|
ToggleDirection => {
|
|
state.mode = !state.mode;
|
|
},
|
|
EnterEditMode => {
|
|
state.entered = true;
|
|
},
|
|
ExitEditMode => {
|
|
state.entered = false;
|
|
},
|
|
TimeZoomOut => {
|
|
let scale = state.time_axis().read().unwrap().scale;
|
|
state.time_axis().write().unwrap().scale = next_note_length(scale)
|
|
},
|
|
TimeZoomIn => {
|
|
let scale = state.time_axis().read().unwrap().scale;
|
|
state.time_axis().write().unwrap().scale = prev_note_length(scale)
|
|
},
|
|
TimeCursorDec => {
|
|
let scale = state.time_axis().read().unwrap().scale;
|
|
state.time_axis().write().unwrap().point_dec(scale);
|
|
},
|
|
TimeCursorInc => {
|
|
let scale = state.time_axis().read().unwrap().scale;
|
|
state.time_axis().write().unwrap().point_inc(scale);
|
|
},
|
|
TimeScrollDec => {
|
|
let scale = state.time_axis().read().unwrap().scale;
|
|
state.time_axis().write().unwrap().start_dec(scale);
|
|
},
|
|
TimeScrollInc => {
|
|
let scale = state.time_axis().read().unwrap().scale;
|
|
state.time_axis().write().unwrap().start_inc(scale);
|
|
},
|
|
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); } }
|
|
},
|
|
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; } }
|
|
},
|
|
NoteScrollDec => {
|
|
state.note_axis().write().unwrap().start_inc(1);
|
|
},
|
|
NoteScrollInc => {
|
|
state.note_axis().write().unwrap().start_dec(1);
|
|
},
|
|
NoteLengthDec => {
|
|
*state.note_len_mut() = prev_note_length(state.note_len())
|
|
},
|
|
NoteLengthInc => {
|
|
*state.note_len_mut() = next_note_length(state.note_len())
|
|
},
|
|
NotePageUp => {
|
|
let mut axis = state.note_axis().write().unwrap();
|
|
axis.start_dec(3);
|
|
axis.point_dec(3);
|
|
},
|
|
NotePageDown => {
|
|
let mut axis = state.note_axis().write().unwrap();
|
|
axis.start_inc(3);
|
|
axis.point_inc(3);
|
|
},
|
|
NoteAppend => {
|
|
if state.entered {
|
|
state.put();
|
|
state.time_cursor_advance();
|
|
}
|
|
},
|
|
NoteSet => {
|
|
if state.entered { state.put(); }
|
|
},
|
|
_ => unreachable!()
|
|
}
|
|
Ok(None)
|
|
}
|
|
}
|