mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-10 21:56:42 +01:00
midi: modularize
This commit is contained in:
parent
39dc6b803e
commit
13444dc59a
29 changed files with 872 additions and 750 deletions
40
crates/midi/src/mode/mode_browse.rs
Normal file
40
crates/midi/src/mode/mode_browse.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
use crate::*;
|
||||
|
||||
atom_command!(FileBrowserCommand: |state: MidiPool| {
|
||||
("begin" [] Some(Self::Begin))
|
||||
("cancel" [] Some(Self::Cancel))
|
||||
("confirm" [] Some(Self::Confirm))
|
||||
("select" [i: usize] Some(Self::Select(i.expect("no index"))))
|
||||
("chdir" [p: PathBuf] Some(Self::Chdir(p.expect("no path"))))
|
||||
("filter" [f: Arc<str>] Some(Self::Filter(f.expect("no filter"))))
|
||||
});
|
||||
|
||||
command!(|self: FileBrowserCommand, state: MidiPool|{
|
||||
use PoolMode::*;
|
||||
use FileBrowserCommand::*;
|
||||
let mode = &mut state.mode;
|
||||
match mode {
|
||||
Some(Import(index, ref mut browser)) => match self {
|
||||
Cancel => { *mode = None; },
|
||||
Chdir(cwd) => { *mode = Some(Import(*index, FileBrowser::new(Some(cwd))?)); },
|
||||
Select(index) => { browser.index = index; },
|
||||
Confirm => if browser.is_file() {
|
||||
let index = *index;
|
||||
let path = browser.path();
|
||||
*mode = None;
|
||||
PoolClipCommand::Import(index, path).execute(state)?;
|
||||
} else if browser.is_dir() {
|
||||
*mode = Some(Import(*index, browser.chdir()?));
|
||||
},
|
||||
_ => todo!(),
|
||||
},
|
||||
Some(Export(index, ref mut browser)) => match self {
|
||||
Cancel => { *mode = None; },
|
||||
Chdir(cwd) => { *mode = Some(Export(*index, FileBrowser::new(Some(cwd))?)); },
|
||||
Select(index) => { browser.index = index; },
|
||||
_ => unreachable!()
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
None
|
||||
});
|
||||
133
crates/midi/src/mode/mode_length.rs
Normal file
133
crates/midi/src/mode/mode_length.rs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
use crate::*;
|
||||
|
||||
/// Displays and edits clip length.
|
||||
#[derive(Clone)]
|
||||
pub struct ClipLength {
|
||||
/// Pulses per beat (quaver)
|
||||
ppq: usize,
|
||||
/// Beats per bar
|
||||
bpb: usize,
|
||||
/// Length of clip in pulses
|
||||
pulses: usize,
|
||||
/// Selected subdivision
|
||||
focus: Option<ClipLengthFocus>,
|
||||
}
|
||||
|
||||
impl ClipLength {
|
||||
fn _new (pulses: usize, focus: Option<ClipLengthFocus>) -> Self {
|
||||
Self { ppq: PPQ, bpb: 4, pulses, focus }
|
||||
}
|
||||
fn bars (&self) -> usize {
|
||||
self.pulses / (self.bpb * self.ppq)
|
||||
}
|
||||
fn beats (&self) -> usize {
|
||||
(self.pulses % (self.bpb * self.ppq)) / self.ppq
|
||||
}
|
||||
fn ticks (&self) -> usize {
|
||||
self.pulses % self.ppq
|
||||
}
|
||||
fn bars_string (&self) -> Arc<str> {
|
||||
format!("{}", self.bars()).into()
|
||||
}
|
||||
fn beats_string (&self) -> Arc<str> {
|
||||
format!("{}", self.beats()).into()
|
||||
}
|
||||
fn ticks_string (&self) -> Arc<str> {
|
||||
format!("{:>02}", self.ticks()).into()
|
||||
}
|
||||
}
|
||||
|
||||
content!(TuiOut: |self: ClipLength| {
|
||||
let bars = ||self.bars_string();
|
||||
let beats = ||self.beats_string();
|
||||
let ticks = ||self.ticks_string();
|
||||
match self.focus {
|
||||
None =>
|
||||
row!(" ", bars(), ".", beats(), ".", ticks()),
|
||||
Some(ClipLengthFocus::Bar) =>
|
||||
row!("[", bars(), "]", beats(), ".", ticks()),
|
||||
Some(ClipLengthFocus::Beat) =>
|
||||
row!(" ", bars(), "[", beats(), "]", ticks()),
|
||||
Some(ClipLengthFocus::Tick) =>
|
||||
row!(" ", bars(), ".", beats(), "[", ticks()),
|
||||
}
|
||||
});
|
||||
|
||||
/// Focused field of `ClipLength`
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ClipLengthFocus {
|
||||
/// Editing the number of bars
|
||||
Bar,
|
||||
/// Editing the number of beats
|
||||
Beat,
|
||||
/// Editing the number of ticks
|
||||
Tick,
|
||||
}
|
||||
|
||||
impl ClipLengthFocus {
|
||||
fn next (&mut self) {
|
||||
*self = match self { Self::Bar => Self::Beat, Self::Beat => Self::Tick, Self::Tick => Self::Bar, }
|
||||
}
|
||||
fn prev (&mut self) {
|
||||
*self = match self { Self::Bar => Self::Tick, Self::Beat => Self::Bar, Self::Tick => Self::Beat, }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ClipLengthCommand {
|
||||
Begin,
|
||||
Cancel,
|
||||
Set(usize),
|
||||
Next,
|
||||
Prev,
|
||||
Inc,
|
||||
Dec,
|
||||
}
|
||||
|
||||
atom_command!(ClipLengthCommand: |state: MidiPool| {
|
||||
("begin" [] Some(Self::Begin))
|
||||
("cancel" [] Some(Self::Cancel))
|
||||
("next" [] Some(Self::Next))
|
||||
("prev" [] Some(Self::Prev))
|
||||
("inc" [] Some(Self::Inc))
|
||||
("dec" [] Some(Self::Dec))
|
||||
("set" [l: usize] Some(Self::Set(l.expect("no length"))))
|
||||
});
|
||||
|
||||
command!(|self: ClipLengthCommand, state: MidiPool|{
|
||||
use ClipLengthCommand::*;
|
||||
use ClipLengthFocus::*;
|
||||
if let Some(
|
||||
PoolMode::Length(clip, ref mut length, ref mut focus)
|
||||
) = state.mode_mut().clone() {
|
||||
match self {
|
||||
Cancel => { *state.mode_mut() = None; },
|
||||
Prev => { focus.prev() },
|
||||
Next => { focus.next() },
|
||||
Inc => match focus {
|
||||
Bar => { *length += 4 * PPQ },
|
||||
Beat => { *length += PPQ },
|
||||
Tick => { *length += 1 },
|
||||
},
|
||||
Dec => match focus {
|
||||
Bar => { *length = length.saturating_sub(4 * PPQ) },
|
||||
Beat => { *length = length.saturating_sub(PPQ) },
|
||||
Tick => { *length = length.saturating_sub(1) },
|
||||
},
|
||||
Set(length) => {
|
||||
let old_length;
|
||||
{
|
||||
let clip = state.clips()[clip].clone();//.write().unwrap();
|
||||
old_length = Some(clip.read().unwrap().length);
|
||||
clip.write().unwrap().length = length;
|
||||
}
|
||||
*state.mode_mut() = None;
|
||||
return Ok(old_length.map(Self::Set))
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
None
|
||||
});
|
||||
38
crates/midi/src/mode/mode_rename.rs
Normal file
38
crates/midi/src/mode/mode_rename.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
use crate::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)] pub enum ClipRenameCommand {
|
||||
Begin,
|
||||
Cancel,
|
||||
Confirm,
|
||||
Set(Arc<str>),
|
||||
}
|
||||
|
||||
atom_command!(ClipRenameCommand: |state: MidiPool| {
|
||||
("begin" [] Some(Self::Begin))
|
||||
("cancel" [] Some(Self::Cancel))
|
||||
("confirm" [] Some(Self::Confirm))
|
||||
("set" [n: Arc<str>] Some(Self::Set(n.expect("no name"))))
|
||||
});
|
||||
|
||||
command!(|self: ClipRenameCommand, state: MidiPool|if let Some(
|
||||
PoolMode::Rename(clip, ref mut old_name)
|
||||
) = state.mode_mut().clone() {
|
||||
match self {
|
||||
Self::Set(s) => {
|
||||
state.clips()[clip].write().unwrap().name = s;
|
||||
return Ok(Some(Self::Set(old_name.clone().into())))
|
||||
},
|
||||
Self::Confirm => {
|
||||
let old_name = old_name.clone();
|
||||
*state.mode_mut() = None;
|
||||
return Ok(Some(Self::Set(old_name)))
|
||||
},
|
||||
Self::Cancel => {
|
||||
state.clips()[clip].write().unwrap().name = old_name.clone().into();
|
||||
return Ok(None)
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue