midi: modularize

This commit is contained in:
🪞👃🪞 2025-04-26 01:29:57 +03:00
parent 39dc6b803e
commit 13444dc59a
29 changed files with 872 additions and 750 deletions

View 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
});

View 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
});

View 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!()
});