mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-01-31 16:36:40 +01:00
midi: modularize
This commit is contained in:
parent
39dc6b803e
commit
13444dc59a
29 changed files with 872 additions and 750 deletions
177
crates/midi/src/pool/pool_api.rs
Normal file
177
crates/midi/src/pool/pool_api.rs
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
use crate::*;
|
||||
|
||||
handle!(TuiIn: |self: MidiPool, input|{
|
||||
Ok(if let Some(command) = match self.mode() {
|
||||
Some(PoolMode::Rename(..)) => self.keys_rename,
|
||||
Some(PoolMode::Length(..)) => self.keys_length,
|
||||
Some(PoolMode::Import(..)) | Some(PoolMode::Export(..)) => self.keys_file,
|
||||
_ => self.keys
|
||||
}.command::<Self, PoolCommand, TuiIn>(self, input) {
|
||||
let _undo = command.execute(self)?;
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
});
|
||||
|
||||
provide!(bool: |self: MidiPool| {});
|
||||
|
||||
provide!(MidiClip: |self: MidiPool| {
|
||||
":new-clip" => self.new_clip(),
|
||||
":cloned-clip" => self.cloned_clip(),
|
||||
});
|
||||
|
||||
provide!(PathBuf: |self: MidiPool| {});
|
||||
|
||||
provide!(Arc<str>: |self: MidiPool| {});
|
||||
|
||||
provide!(usize: |self: MidiPool| {
|
||||
":current" => 0,
|
||||
":after" => 0,
|
||||
":previous" => 0,
|
||||
":next" => 0
|
||||
});
|
||||
|
||||
provide!(ItemColor: |self: MidiPool| {
|
||||
":random-color" => ItemColor::random()
|
||||
});
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)] pub enum PoolCommand {
|
||||
/// Toggle visibility of pool
|
||||
Show(bool),
|
||||
/// Select a clip from the clip pool
|
||||
Select(usize),
|
||||
/// Rename a clip
|
||||
Rename(ClipRenameCommand),
|
||||
/// Change the length of a clip
|
||||
Length(ClipLengthCommand),
|
||||
/// Import from file
|
||||
Import(FileBrowserCommand),
|
||||
/// Export to file
|
||||
Export(FileBrowserCommand),
|
||||
/// Update the contents of the clip pool
|
||||
Clip(PoolClipCommand),
|
||||
}
|
||||
|
||||
atom_command!(PoolCommand: |state: MidiPool| {
|
||||
("show" [a: bool] Some(Self::Show(a.expect("no flag"))))
|
||||
("select" [i: usize] Some(Self::Select(i.expect("no index"))))
|
||||
("rename" [,..a] ClipRenameCommand::try_from_expr(state, a).map(Self::Rename))
|
||||
("length" [,..a] ClipLengthCommand::try_from_expr(state, a).map(Self::Length))
|
||||
("import" [,..a] FileBrowserCommand::try_from_expr(state, a).map(Self::Import))
|
||||
("export" [,..a] FileBrowserCommand::try_from_expr(state, a).map(Self::Export))
|
||||
("clip" [,..a] PoolClipCommand::try_from_expr(state, a).map(Self::Clip))
|
||||
});
|
||||
|
||||
command!(|self: PoolCommand, state: MidiPool|{
|
||||
use PoolCommand::*;
|
||||
match self {
|
||||
Rename(ClipRenameCommand::Begin) => { state.begin_clip_rename(); None }
|
||||
Rename(command) => command.delegate(state, Rename)?,
|
||||
Length(ClipLengthCommand::Begin) => { state.begin_clip_length(); None },
|
||||
Length(command) => command.delegate(state, Length)?,
|
||||
Import(FileBrowserCommand::Begin) => { state.begin_import()?; None },
|
||||
Import(command) => command.delegate(state, Import)?,
|
||||
Export(FileBrowserCommand::Begin) => { state.begin_export()?; None },
|
||||
Export(command) => command.delegate(state, Export)?,
|
||||
Clip(command) => command.execute(state)?.map(Clip),
|
||||
Show(visible) => { state.visible = visible; Some(Self::Show(!visible)) },
|
||||
Select(clip) => { state.set_clip_index(clip); None },
|
||||
}
|
||||
});
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)] pub enum PoolClipCommand {
|
||||
Add(usize, MidiClip),
|
||||
Delete(usize),
|
||||
Swap(usize, usize),
|
||||
Import(usize, PathBuf),
|
||||
Export(usize, PathBuf),
|
||||
SetName(usize, Arc<str>),
|
||||
SetLength(usize, usize),
|
||||
SetColor(usize, ItemColor),
|
||||
}
|
||||
|
||||
atom_command!(PoolClipCommand: |state: MidiPool| {
|
||||
("add" [i: usize, c: MidiClip]
|
||||
Some(Self::Add(i.expect("no index"), c.expect("no clip"))))
|
||||
("delete" [i: usize]
|
||||
Some(Self::Delete(i.expect("no index"))))
|
||||
("swap" [a: usize, b: usize]
|
||||
Some(Self::Swap(a.expect("no index"), b.expect("no index"))))
|
||||
("import" [i: usize, p: PathBuf]
|
||||
Some(Self::Import(i.expect("no index"), p.expect("no path"))))
|
||||
("export" [i: usize, p: PathBuf]
|
||||
Some(Self::Export(i.expect("no index"), p.expect("no path"))))
|
||||
("set-name" [i: usize, n: Arc<str>]
|
||||
Some(Self::SetName(i.expect("no index"), n.expect("no name"))))
|
||||
("set-length" [i: usize, l: usize]
|
||||
Some(Self::SetLength(i.expect("no index"), l.expect("no length"))))
|
||||
("set-color" [i: usize, c: ItemColor]
|
||||
Some(Self::SetColor(i.expect("no index"), c.expect("no color"))))
|
||||
});
|
||||
|
||||
impl<T: HasClips> Command<T> for PoolClipCommand {
|
||||
fn execute (self, model: &mut T) -> Perhaps<Self> {
|
||||
use PoolClipCommand::*;
|
||||
Ok(match self {
|
||||
Add(mut index, clip) => {
|
||||
let clip = Arc::new(RwLock::new(clip));
|
||||
let mut clips = model.clips_mut();
|
||||
if index >= clips.len() {
|
||||
index = clips.len();
|
||||
clips.push(clip)
|
||||
} else {
|
||||
clips.insert(index, clip);
|
||||
}
|
||||
Some(Self::Delete(index))
|
||||
},
|
||||
Delete(index) => {
|
||||
let clip = model.clips_mut().remove(index).read().unwrap().clone();
|
||||
Some(Self::Add(index, clip))
|
||||
},
|
||||
Swap(index, other) => {
|
||||
model.clips_mut().swap(index, other);
|
||||
Some(Self::Swap(index, other))
|
||||
},
|
||||
Import(index, path) => {
|
||||
let bytes = std::fs::read(&path)?;
|
||||
let smf = Smf::parse(bytes.as_slice())?;
|
||||
let mut t = 0u32;
|
||||
let mut events = vec![];
|
||||
for track in smf.tracks.iter() {
|
||||
for event in track.iter() {
|
||||
t += event.delta.as_int();
|
||||
if let TrackEventKind::Midi { channel, message } = event.kind {
|
||||
events.push((t, channel.as_int(), message));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut clip = MidiClip::new("imported", true, t as usize + 1, None, None);
|
||||
for event in events.iter() {
|
||||
clip.notes[event.0 as usize].push(event.2);
|
||||
}
|
||||
Self::Add(index, clip).execute(model)?
|
||||
},
|
||||
Export(_index, _path) => {
|
||||
todo!("export clip to midi file");
|
||||
},
|
||||
SetName(index, name) => {
|
||||
let clip = &mut model.clips_mut()[index];
|
||||
let old_name = clip.read().unwrap().name.clone();
|
||||
clip.write().unwrap().name = name;
|
||||
Some(Self::SetName(index, old_name))
|
||||
},
|
||||
SetLength(index, length) => {
|
||||
let clip = &mut model.clips_mut()[index];
|
||||
let old_len = clip.read().unwrap().length;
|
||||
clip.write().unwrap().length = length;
|
||||
Some(Self::SetLength(index, old_len))
|
||||
},
|
||||
SetColor(index, color) => {
|
||||
let mut color = ItemPalette::from(color);
|
||||
std::mem::swap(&mut color, &mut model.clips()[index].write().unwrap().color);
|
||||
Some(Self::SetColor(index, color.base))
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue