mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
use edn_command on all midi pool commands
This commit is contained in:
parent
efbabe6248
commit
50b7d8a23d
8 changed files with 194 additions and 188 deletions
|
|
@ -4,14 +4,14 @@ use crate::*;
|
||||||
($lt:lifetime: $type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
|
($lt:lifetime: $type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
|
||||||
impl<$lt> EdnProvide<$lt, $type> for $State {
|
impl<$lt> EdnProvide<$lt, $type> for $State {
|
||||||
fn get <S: AsRef<str>> (&$lt $self, edn: &$lt EdnItem<S>) -> Option<$type> {
|
fn get <S: AsRef<str>> (&$lt $self, edn: &$lt EdnItem<S>) -> Option<$type> {
|
||||||
Some(match edn.to_ref() { $(EdnItem::Sym($pat) => $expr),*, _ => return None })
|
Some(match edn.to_ref() { $(EdnItem::Sym($pat) => $expr,)* _ => return None })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
|
($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
|
||||||
impl<'a> EdnProvide<'a, $type> for $State {
|
impl<'a> EdnProvide<'a, $type> for $State {
|
||||||
fn get <S: AsRef<str>> (&'a $self, edn: &'a EdnItem<S>) -> Option<$type> {
|
fn get <S: AsRef<str>> (&'a $self, edn: &'a EdnItem<S>) -> Option<$type> {
|
||||||
Some(match edn.to_ref() { $(EdnItem::Sym($pat) => $expr),*, _ => return None })
|
Some(match edn.to_ref() { $(EdnItem::Sym($pat) => $expr,)* _ => return None })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,8 @@ mod midi_pool; pub use midi_pool::*;
|
||||||
mod midi_pool_tui; pub use midi_pool_tui::*;
|
mod midi_pool_tui; pub use midi_pool_tui::*;
|
||||||
mod midi_pool_cmd; pub use midi_pool_cmd::*;
|
mod midi_pool_cmd; pub use midi_pool_cmd::*;
|
||||||
|
|
||||||
mod midi_edit; pub use midi_edit::*;
|
mod midi_edit; pub use midi_edit::*;
|
||||||
mod midi_edit_cmd; pub use midi_edit_cmd::*;
|
mod piano_h; pub use self::piano_h::*;
|
||||||
mod midi_edit_tui; pub use midi_edit_tui::*;
|
|
||||||
|
|
||||||
mod piano_h; pub use self::piano_h::*;
|
|
||||||
|
|
||||||
pub(crate) use ::tek_time::*;
|
pub(crate) use ::tek_time::*;
|
||||||
pub(crate) use ::tek_jack::{*, jack::*};
|
pub(crate) use ::tek_jack::{*, jack::*};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use self::MidiEditCommand::*;
|
||||||
|
use KeyCode::*;
|
||||||
pub trait HasEditor {
|
pub trait HasEditor {
|
||||||
fn editor (&self) -> &Option<MidiEditor>;
|
fn editor (&self) -> &Option<MidiEditor>;
|
||||||
fn editor_mut (&mut self) -> &Option<MidiEditor>;
|
fn editor_mut (&mut self) -> &Option<MidiEditor>;
|
||||||
|
|
@ -46,7 +48,7 @@ impl Default for MidiEditor {
|
||||||
mode: PianoHorizontal::new(None),
|
mode: PianoHorizontal::new(None),
|
||||||
size: Measure::new(),
|
size: Measure::new(),
|
||||||
keymap: EdnKeymap(
|
keymap: EdnKeymap(
|
||||||
EdnItem::<String>::read_all(include_str!("midi_editor_keys.edn"))
|
EdnItem::<String>::read_all(include_str!("midi_edit_keys.edn"))
|
||||||
.expect("failed to load keymap for MidiEditor")
|
.expect("failed to load keymap for MidiEditor")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -126,3 +128,142 @@ impl MidiViewer for MidiEditor {
|
||||||
fn clip_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>> { self.mode.clip_mut() }
|
fn clip_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>> { self.mode.clip_mut() }
|
||||||
fn set_clip (&mut self, p: Option<&Arc<RwLock<MidiClip>>>) { self.mode.set_clip(p) }
|
fn set_clip (&mut self, p: Option<&Arc<RwLock<MidiClip>>>) { self.mode.set_clip(p) }
|
||||||
}
|
}
|
||||||
|
has_size!(<TuiOut>|self: MidiEditor|&self.size);
|
||||||
|
content!(TuiOut: |self: MidiEditor| {
|
||||||
|
self.autoscroll();
|
||||||
|
//self.autozoom();
|
||||||
|
self.size.of(&self.mode)
|
||||||
|
});
|
||||||
|
impl MidiEditor {
|
||||||
|
pub fn clip_status (&self) -> impl Content<TuiOut> + '_ {
|
||||||
|
let (color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
|
||||||
|
(clip.color, clip.name.clone(), clip.length, clip.looped)
|
||||||
|
} else {
|
||||||
|
(ItemPalette::from(TuiTheme::g(64)), String::new().into(), 0, false)
|
||||||
|
};
|
||||||
|
row!(
|
||||||
|
FieldV(color, "Edit", format!("{name} ({length})")),
|
||||||
|
FieldV(color, "Loop", looped.to_string())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn edit_status (&self) -> impl Content<TuiOut> + '_ {
|
||||||
|
let (color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
|
||||||
|
(clip.color, clip.length)
|
||||||
|
} else {
|
||||||
|
(ItemPalette::from(TuiTheme::g(64)), 0)
|
||||||
|
};
|
||||||
|
let time_point = self.time_point();
|
||||||
|
let time_zoom = self.time_zoom().get();
|
||||||
|
let time_lock = if self.time_lock().get() { "[lock]" } else { " " };
|
||||||
|
let note_point = format!("{:>3}", self.note_point());
|
||||||
|
let note_name = format!("{:4}", Note::pitch_to_name(self.note_point()));
|
||||||
|
let note_len = format!("{:>4}", self.note_len());
|
||||||
|
Bsp::e(
|
||||||
|
FieldV(color, "Time", format!("{length}/{time_zoom}+{time_point} {time_lock}")),
|
||||||
|
FieldV(color, "Note", format!("{note_name} {note_point} {note_len}")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
edn_provide!(bool: |self: MidiEditor|{
|
||||||
|
":true" => true,
|
||||||
|
":false" => false
|
||||||
|
});
|
||||||
|
edn_provide!(usize: |self: MidiEditor|{
|
||||||
|
":note-length" => self.note_len(),
|
||||||
|
":note-point" => self.note_point(),
|
||||||
|
":time-point" => self.time_point(),
|
||||||
|
":time-zoom" => self.time_zoom().get(),
|
||||||
|
});
|
||||||
|
edn_command!(MidiEditCommand: |state: MidiEditor| {
|
||||||
|
("note/put" [a: bool] Self::PutNote)
|
||||||
|
("note/del" [a: bool] Self::PutNote)
|
||||||
|
("note/pos" [a: usize] Self::SetNoteCursor(a.expect("no note cursor")))
|
||||||
|
("note/len" [a: usize] Self::SetNoteLength(a.expect("no note length")))
|
||||||
|
("time/pos" [a: usize] Self::SetTimeCursor(a.expect("no time cursor")))
|
||||||
|
("time/zoom" [a: usize] Self::SetTimeZoom(a.expect("no time zoom")))
|
||||||
|
("time/lock" [a: bool] Self::SetTimeLock(a.expect("no time lock")))
|
||||||
|
});
|
||||||
|
//impl EdnCommand<MidiEditor> for MidiEditCommand {
|
||||||
|
//fn from_edn <'a> (state: &MidiEditor, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
||||||
|
//use EdnItem::*;
|
||||||
|
//match (head, tail) {
|
||||||
|
//(Key("note/put"), [a]) => Self::PutNote,
|
||||||
|
//(Key("note/del"), [a]) => Self::AppendNote,
|
||||||
|
//(Key("note/dur"), [a]) => Self::AppendNote,
|
||||||
|
//(Key("note/range"), [a]) => Self::AppendNote,
|
||||||
|
//(Key("note/pos"), [a]) => Self::AppendNote,
|
||||||
|
//(Key("time/pos"), [a]) => Self::AppendNote,
|
||||||
|
//(Key("time/zoom"), [a]) => Self::AppendNote,
|
||||||
|
//(Key("time/lock"), [a]) => Self::AppendNote,
|
||||||
|
//_ => todo!()
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
#[derive(Clone, Debug)] pub enum MidiEditCommand {
|
||||||
|
// TODO: 1-9 seek markers that by default start every 8th of the clip
|
||||||
|
AppendNote,
|
||||||
|
PutNote,
|
||||||
|
SetNoteCursor(usize),
|
||||||
|
SetNoteLength(usize),
|
||||||
|
SetNoteScroll(usize),
|
||||||
|
SetTimeCursor(usize),
|
||||||
|
SetTimeScroll(usize),
|
||||||
|
SetTimeZoom(usize),
|
||||||
|
SetTimeLock(bool),
|
||||||
|
Show(Option<Arc<RwLock<MidiClip>>>),
|
||||||
|
}
|
||||||
|
handle!(TuiIn: |self: MidiEditor, input|MidiEditCommand::execute_with_state(self, input.event()));
|
||||||
|
keymap!(KEYS_MIDI_EDITOR = |s: MidiEditor, _input: Event| MidiEditCommand {
|
||||||
|
key(Up) => SetNoteCursor(s.note_point() + 1),
|
||||||
|
key(Char('w')) => SetNoteCursor(s.note_point() + 1),
|
||||||
|
key(Down) => SetNoteCursor(s.note_point().saturating_sub(1)),
|
||||||
|
key(Char('s')) => SetNoteCursor(s.note_point().saturating_sub(1)),
|
||||||
|
key(Left) => SetTimeCursor(s.time_point().saturating_sub(s.note_len())),
|
||||||
|
key(Char('a')) => SetTimeCursor(s.time_point().saturating_sub(s.note_len())),
|
||||||
|
key(Right) => SetTimeCursor((s.time_point() + s.note_len()) % s.clip_length()),
|
||||||
|
ctrl(alt(key(Up))) => SetNoteScroll(s.note_point() + 3),
|
||||||
|
ctrl(alt(key(Down))) => SetNoteScroll(s.note_point().saturating_sub(3)),
|
||||||
|
ctrl(alt(key(Left))) => SetTimeScroll(s.time_point().saturating_sub(s.time_zoom().get())),
|
||||||
|
ctrl(alt(key(Right))) => SetTimeScroll((s.time_point() + s.time_zoom().get()) % s.clip_length()),
|
||||||
|
ctrl(key(Up)) => SetNoteScroll(s.note_lo().get() + 1),
|
||||||
|
ctrl(key(Down)) => SetNoteScroll(s.note_lo().get().saturating_sub(1)),
|
||||||
|
ctrl(key(Left)) => SetTimeScroll(s.time_start().get().saturating_sub(s.note_len())),
|
||||||
|
ctrl(key(Right)) => SetTimeScroll(s.time_start().get() + s.note_len()),
|
||||||
|
alt(key(Up)) => SetNoteCursor(s.note_point() + 3),
|
||||||
|
alt(key(Down)) => SetNoteCursor(s.note_point().saturating_sub(3)),
|
||||||
|
alt(key(Left)) => SetTimeCursor(s.time_point().saturating_sub(s.time_zoom().get())),
|
||||||
|
alt(key(Right)) => SetTimeCursor((s.time_point() + s.time_zoom().get()) % s.clip_length()),
|
||||||
|
key(Char('d')) => SetTimeCursor((s.time_point() + s.note_len()) % s.clip_length()),
|
||||||
|
key(Char('z')) => SetTimeLock(!s.time_lock().get()),
|
||||||
|
key(Char('-')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::next(s.time_zoom().get()) }),
|
||||||
|
key(Char('_')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::next(s.time_zoom().get()) }),
|
||||||
|
key(Char('=')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::prev(s.time_zoom().get()) }),
|
||||||
|
key(Char('+')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::prev(s.time_zoom().get()) }),
|
||||||
|
key(Enter) => PutNote,
|
||||||
|
ctrl(key(Enter)) => AppendNote,
|
||||||
|
key(Char(',')) => SetNoteLength(NoteDuration::prev(s.note_len())),
|
||||||
|
key(Char('.')) => SetNoteLength(NoteDuration::next(s.note_len())),
|
||||||
|
key(Char('<')) => SetNoteLength(NoteDuration::prev(s.note_len())),
|
||||||
|
key(Char('>')) => SetNoteLength(NoteDuration::next(s.note_len())),
|
||||||
|
//// TODO: kpat!(Char('/')) => // toggle 3plet
|
||||||
|
//// TODO: kpat!(Char('?')) => // toggle dotted
|
||||||
|
});
|
||||||
|
impl Command<MidiEditor> for MidiEditCommand {
|
||||||
|
fn execute (self, state: &mut MidiEditor) -> Perhaps<Self> {
|
||||||
|
use MidiEditCommand::*;
|
||||||
|
match self {
|
||||||
|
Show(clip) => { state.set_clip(clip.as_ref()); },
|
||||||
|
PutNote => { state.put_note(false); },
|
||||||
|
AppendNote => { state.put_note(true); },
|
||||||
|
SetTimeZoom(x) => { state.time_zoom().set(x); state.redraw(); },
|
||||||
|
SetTimeLock(x) => { state.time_lock().set(x); },
|
||||||
|
SetTimeScroll(x) => { state.time_start().set(x); },
|
||||||
|
SetNoteScroll(x) => { state.note_lo().set(x.min(127)); },
|
||||||
|
SetNoteLength(x) => { state.set_note_len(x); },
|
||||||
|
SetTimeCursor(x) => { state.set_time_point(x); },
|
||||||
|
SetNoteCursor(note) => { state.set_note_point(note.min(127)); },
|
||||||
|
_ => todo!("{:?}", self)
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
use self::MidiEditCommand::*;
|
|
||||||
use KeyCode::*;
|
|
||||||
edn_provide!(bool: |self: MidiEditor|{
|
|
||||||
":true" => true,
|
|
||||||
":false" => false
|
|
||||||
});
|
|
||||||
edn_provide!(usize: |self: MidiEditor|{
|
|
||||||
":note-length" => self.note_len(),
|
|
||||||
":note-point" => self.note_point(),
|
|
||||||
":time-point" => self.time_point(),
|
|
||||||
":time-zoom" => self.time_zoom().get(),
|
|
||||||
});
|
|
||||||
edn_command!(MidiEditCommand: |state: MidiEditor| {
|
|
||||||
("note/put" [a: bool] Self::PutNote)
|
|
||||||
("note/del" [a: bool] Self::PutNote)
|
|
||||||
("note/pos" [a: usize] Self::SetNoteCursor(a.expect("no note cursor")))
|
|
||||||
("note/len" [a: usize] Self::SetNoteLength(a.expect("no note length")))
|
|
||||||
("time/pos" [a: usize] Self::SetTimeCursor(a.expect("no time cursor")))
|
|
||||||
("time/zoom" [a: usize] Self::SetTimeZoom(a.expect("no time zoom")))
|
|
||||||
("time/lock" [a: bool] Self::SetTimeLock(a.expect("no time lock")))
|
|
||||||
});
|
|
||||||
//impl EdnCommand<MidiEditor> for MidiEditCommand {
|
|
||||||
//fn from_edn <'a> (state: &MidiEditor, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
|
||||||
//use EdnItem::*;
|
|
||||||
//match (head, tail) {
|
|
||||||
//(Key("note/put"), [a]) => Self::PutNote,
|
|
||||||
//(Key("note/del"), [a]) => Self::AppendNote,
|
|
||||||
//(Key("note/dur"), [a]) => Self::AppendNote,
|
|
||||||
//(Key("note/range"), [a]) => Self::AppendNote,
|
|
||||||
//(Key("note/pos"), [a]) => Self::AppendNote,
|
|
||||||
//(Key("time/pos"), [a]) => Self::AppendNote,
|
|
||||||
//(Key("time/zoom"), [a]) => Self::AppendNote,
|
|
||||||
//(Key("time/lock"), [a]) => Self::AppendNote,
|
|
||||||
//_ => todo!()
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
#[derive(Clone, Debug)] pub enum MidiEditCommand {
|
|
||||||
// TODO: 1-9 seek markers that by default start every 8th of the clip
|
|
||||||
AppendNote,
|
|
||||||
PutNote,
|
|
||||||
SetNoteCursor(usize),
|
|
||||||
SetNoteLength(usize),
|
|
||||||
SetNoteScroll(usize),
|
|
||||||
SetTimeCursor(usize),
|
|
||||||
SetTimeScroll(usize),
|
|
||||||
SetTimeZoom(usize),
|
|
||||||
SetTimeLock(bool),
|
|
||||||
Show(Option<Arc<RwLock<MidiClip>>>),
|
|
||||||
}
|
|
||||||
handle!(TuiIn: |self: MidiEditor, input|MidiEditCommand::execute_with_state(self, input.event()));
|
|
||||||
keymap!(KEYS_MIDI_EDITOR = |s: MidiEditor, _input: Event| MidiEditCommand {
|
|
||||||
key(Up) => SetNoteCursor(s.note_point() + 1),
|
|
||||||
key(Char('w')) => SetNoteCursor(s.note_point() + 1),
|
|
||||||
key(Down) => SetNoteCursor(s.note_point().saturating_sub(1)),
|
|
||||||
key(Char('s')) => SetNoteCursor(s.note_point().saturating_sub(1)),
|
|
||||||
key(Left) => SetTimeCursor(s.time_point().saturating_sub(s.note_len())),
|
|
||||||
key(Char('a')) => SetTimeCursor(s.time_point().saturating_sub(s.note_len())),
|
|
||||||
key(Right) => SetTimeCursor((s.time_point() + s.note_len()) % s.clip_length()),
|
|
||||||
ctrl(alt(key(Up))) => SetNoteScroll(s.note_point() + 3),
|
|
||||||
ctrl(alt(key(Down))) => SetNoteScroll(s.note_point().saturating_sub(3)),
|
|
||||||
ctrl(alt(key(Left))) => SetTimeScroll(s.time_point().saturating_sub(s.time_zoom().get())),
|
|
||||||
ctrl(alt(key(Right))) => SetTimeScroll((s.time_point() + s.time_zoom().get()) % s.clip_length()),
|
|
||||||
ctrl(key(Up)) => SetNoteScroll(s.note_lo().get() + 1),
|
|
||||||
ctrl(key(Down)) => SetNoteScroll(s.note_lo().get().saturating_sub(1)),
|
|
||||||
ctrl(key(Left)) => SetTimeScroll(s.time_start().get().saturating_sub(s.note_len())),
|
|
||||||
ctrl(key(Right)) => SetTimeScroll(s.time_start().get() + s.note_len()),
|
|
||||||
alt(key(Up)) => SetNoteCursor(s.note_point() + 3),
|
|
||||||
alt(key(Down)) => SetNoteCursor(s.note_point().saturating_sub(3)),
|
|
||||||
alt(key(Left)) => SetTimeCursor(s.time_point().saturating_sub(s.time_zoom().get())),
|
|
||||||
alt(key(Right)) => SetTimeCursor((s.time_point() + s.time_zoom().get()) % s.clip_length()),
|
|
||||||
key(Char('d')) => SetTimeCursor((s.time_point() + s.note_len()) % s.clip_length()),
|
|
||||||
key(Char('z')) => SetTimeLock(!s.time_lock().get()),
|
|
||||||
key(Char('-')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::next(s.time_zoom().get()) }),
|
|
||||||
key(Char('_')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::next(s.time_zoom().get()) }),
|
|
||||||
key(Char('=')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::prev(s.time_zoom().get()) }),
|
|
||||||
key(Char('+')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::prev(s.time_zoom().get()) }),
|
|
||||||
key(Enter) => PutNote,
|
|
||||||
ctrl(key(Enter)) => AppendNote,
|
|
||||||
key(Char(',')) => SetNoteLength(NoteDuration::prev(s.note_len())),
|
|
||||||
key(Char('.')) => SetNoteLength(NoteDuration::next(s.note_len())),
|
|
||||||
key(Char('<')) => SetNoteLength(NoteDuration::prev(s.note_len())),
|
|
||||||
key(Char('>')) => SetNoteLength(NoteDuration::next(s.note_len())),
|
|
||||||
//// TODO: kpat!(Char('/')) => // toggle 3plet
|
|
||||||
//// TODO: kpat!(Char('?')) => // toggle dotted
|
|
||||||
});
|
|
||||||
impl Command<MidiEditor> for MidiEditCommand {
|
|
||||||
fn execute (self, state: &mut MidiEditor) -> Perhaps<Self> {
|
|
||||||
use MidiEditCommand::*;
|
|
||||||
match self {
|
|
||||||
Show(clip) => { state.set_clip(clip.as_ref()); },
|
|
||||||
PutNote => { state.put_note(false); },
|
|
||||||
AppendNote => { state.put_note(true); },
|
|
||||||
SetTimeZoom(x) => { state.time_zoom().set(x); state.redraw(); },
|
|
||||||
SetTimeLock(x) => { state.time_lock().set(x); },
|
|
||||||
SetTimeScroll(x) => { state.time_start().set(x); },
|
|
||||||
SetNoteScroll(x) => { state.note_lo().set(x.min(127)); },
|
|
||||||
SetNoteLength(x) => { state.set_note_len(x); },
|
|
||||||
SetTimeCursor(x) => { state.set_time_point(x); },
|
|
||||||
SetNoteCursor(note) => { state.set_note_point(note.min(127)); },
|
|
||||||
_ => todo!("{:?}", self)
|
|
||||||
}
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
has_size!(<TuiOut>|self: MidiEditor|&self.size);
|
|
||||||
content!(TuiOut: |self: MidiEditor| {
|
|
||||||
self.autoscroll();
|
|
||||||
//self.autozoom();
|
|
||||||
self.size.of(&self.mode)
|
|
||||||
});
|
|
||||||
impl MidiEditor {
|
|
||||||
pub fn clip_status (&self) -> impl Content<TuiOut> + '_ {
|
|
||||||
let (color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
|
|
||||||
(clip.color, clip.name.clone(), clip.length, clip.looped)
|
|
||||||
} else {
|
|
||||||
(ItemPalette::from(TuiTheme::g(64)), String::new().into(), 0, false)
|
|
||||||
};
|
|
||||||
row!(
|
|
||||||
FieldV(color, "Edit", format!("{name} ({length})")),
|
|
||||||
FieldV(color, "Loop", looped.to_string())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
pub fn edit_status (&self) -> impl Content<TuiOut> + '_ {
|
|
||||||
let (color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
|
|
||||||
(clip.color, clip.length)
|
|
||||||
} else {
|
|
||||||
(ItemPalette::from(TuiTheme::g(64)), 0)
|
|
||||||
};
|
|
||||||
let time_point = self.time_point();
|
|
||||||
let time_zoom = self.time_zoom().get();
|
|
||||||
let time_lock = if self.time_lock().get() { "[lock]" } else { " " };
|
|
||||||
let note_point = format!("{:>3}", self.note_point());
|
|
||||||
let note_name = format!("{:4}", Note::pitch_to_name(self.note_point()));
|
|
||||||
let note_len = format!("{:>4}", self.note_len());
|
|
||||||
Bsp::e(
|
|
||||||
FieldV(color, "Time", format!("{length}/{time_zoom}+{time_point} {time_lock}")),
|
|
||||||
FieldV(color, "Note", format!("{note_name} {note_point} {note_len}")),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +1,8 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub trait MidiPlayerApi: MidiRecordApi + MidiPlaybackApi + Send + Sync {}
|
|
||||||
|
|
||||||
impl MidiPlayerApi for MidiPlayer {}
|
|
||||||
|
|
||||||
pub trait HasPlayer {
|
pub trait HasPlayer {
|
||||||
fn player (&self) -> &impl MidiPlayerApi;
|
fn player (&self) -> &impl MidiPlayerApi;
|
||||||
fn player_mut (&mut self) -> &mut impl MidiPlayerApi;
|
fn player_mut (&mut self) -> &mut impl MidiPlayerApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export] macro_rules! has_player {
|
#[macro_export] macro_rules! has_player {
|
||||||
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
||||||
impl $(<$($L),*$($T $(: $U)?),*>)? HasPlayer for $Struct $(<$($L),*$($T),*>)? {
|
impl $(<$($L),*$($T $(: $U)?),*>)? HasPlayer for $Struct $(<$($L),*$($T),*>)? {
|
||||||
|
|
@ -17,7 +11,8 @@ pub trait HasPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub trait MidiPlayerApi: MidiRecordApi + MidiPlaybackApi + Send + Sync {}
|
||||||
|
impl MidiPlayerApi for MidiPlayer {}
|
||||||
/// Contains state for playing a clip
|
/// Contains state for playing a clip
|
||||||
pub struct MidiPlayer {
|
pub struct MidiPlayer {
|
||||||
/// State of clock and playhead
|
/// State of clock and playhead
|
||||||
|
|
@ -83,17 +78,11 @@ impl MidiPlayer {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn play_status (&self) -> impl Content<TuiOut> {
|
|
||||||
ClipSelected::play_clip(self)
|
|
||||||
}
|
|
||||||
pub fn next_status (&self) -> impl Content<TuiOut> {
|
|
||||||
ClipSelected::next_clip(self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl std::fmt::Debug for MidiPlayer {
|
impl std::fmt::Debug for MidiPlayer {
|
||||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
f.debug_struct("MidiPlayer")
|
f.debug_struct("MidiPlayer")
|
||||||
.field("clock", &self.clock)
|
.field("clock", &self.clock)
|
||||||
.field("play_clip", &self.play_clip)
|
.field("play_clip", &self.play_clip)
|
||||||
.field("next_clip", &self.next_clip)
|
.field("next_clip", &self.next_clip)
|
||||||
.finish()
|
.finish()
|
||||||
|
|
@ -129,7 +118,6 @@ impl HasMidiOuts for MidiPlayer {
|
||||||
fn midi_outs_mut (&mut self) -> &mut Vec<JackPort<MidiOut>> { &mut self.midi_outs }
|
fn midi_outs_mut (&mut self) -> &mut Vec<JackPort<MidiOut>> { &mut self.midi_outs }
|
||||||
fn midi_note (&mut self) -> &mut Vec<u8> { &mut self.note_buf }
|
fn midi_note (&mut self) -> &mut Vec<u8> { &mut self.note_buf }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hosts the JACK callback for a single MIDI player
|
/// Hosts the JACK callback for a single MIDI player
|
||||||
pub struct PlayerAudio<'a, T: MidiPlayerApi>(
|
pub struct PlayerAudio<'a, T: MidiPlayerApi>(
|
||||||
/// Player
|
/// Player
|
||||||
|
|
@ -139,7 +127,6 @@ pub struct PlayerAudio<'a, T: MidiPlayerApi>(
|
||||||
/// Note chunk buffer
|
/// Note chunk buffer
|
||||||
pub &'a mut Vec<Vec<Vec<u8>>>,
|
pub &'a mut Vec<Vec<Vec<u8>>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// JACK process callback for a sequencer's clip player/recorder.
|
/// JACK process callback for a sequencer's clip player/recorder.
|
||||||
impl<T: MidiPlayerApi> Audio for PlayerAudio<'_, T> {
|
impl<T: MidiPlayerApi> Audio for PlayerAudio<'_, T> {
|
||||||
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||||
|
|
@ -166,7 +153,6 @@ impl<T: MidiPlayerApi> Audio for PlayerAudio<'_, T> {
|
||||||
Control::Continue
|
Control::Continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MidiRecordApi for MidiPlayer {
|
impl MidiRecordApi for MidiPlayer {
|
||||||
fn recording (&self) -> bool {
|
fn recording (&self) -> bool {
|
||||||
self.recording
|
self.recording
|
||||||
|
|
@ -190,13 +176,11 @@ impl MidiRecordApi for MidiPlayer {
|
||||||
&self.notes_in
|
&self.notes_in
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MidiPlaybackApi for MidiPlayer {
|
impl MidiPlaybackApi for MidiPlayer {
|
||||||
fn notes_out (&self) -> &Arc<RwLock<[bool; 128]>> {
|
fn notes_out (&self) -> &Arc<RwLock<[bool; 128]>> {
|
||||||
&self.notes_out
|
&self.notes_out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasPlayClip for MidiPlayer {
|
impl HasPlayClip for MidiPlayer {
|
||||||
fn reset (&self) -> bool {
|
fn reset (&self) -> bool {
|
||||||
self.reset
|
self.reset
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use KeyCode::*;
|
use KeyCode::*;
|
||||||
|
edn_provide!(bool: |self: MidiPool| {});
|
||||||
|
edn_provide!(usize: |self: MidiPool| {});
|
||||||
|
edn_provide!(MidiClip: |self: MidiPool| {});
|
||||||
|
edn_provide!(PathBuf: |self: MidiPool| {});
|
||||||
|
edn_provide!(Arc<str>: |self: MidiPool| {});
|
||||||
|
edn_provide!(ItemColor: |self: MidiPool| {});
|
||||||
#[derive(Clone, PartialEq, Debug)] pub enum PoolCommand {
|
#[derive(Clone, PartialEq, Debug)] pub enum PoolCommand {
|
||||||
Show(bool),
|
Show(bool),
|
||||||
/// Update the contents of the clip pool
|
/// Update the contents of the clip pool
|
||||||
|
|
@ -20,7 +26,7 @@ edn_command!(PoolCommand: |state: MidiPool| {
|
||||||
("select" [i: usize] Self::Select(i.expect("no index")))
|
("select" [i: usize] Self::Select(i.expect("no index")))
|
||||||
("clip" [a, ..b] Self::Clip(PoolClipCommand::from_edn(state, &a.to_ref(), b)))
|
("clip" [a, ..b] Self::Clip(PoolClipCommand::from_edn(state, &a.to_ref(), b)))
|
||||||
("rename" [a, ..b] Self::Rename(ClipRenameCommand::from_edn(state, &a.to_ref(), b)))
|
("rename" [a, ..b] Self::Rename(ClipRenameCommand::from_edn(state, &a.to_ref(), b)))
|
||||||
("length" [a, ..b] Self::Length(ClipRenameCommand::from_edn(state, &a.to_ref(), b)))
|
("length" [a, ..b] Self::Length(ClipLengthCommand::from_edn(state, &a.to_ref(), b)))
|
||||||
("import" [a, ..b] Self::Import(FileBrowserCommand::from_edn(state, &a.to_ref(), b)))
|
("import" [a, ..b] Self::Import(FileBrowserCommand::from_edn(state, &a.to_ref(), b)))
|
||||||
("export" [a, ..b] Self::Export(FileBrowserCommand::from_edn(state, &a.to_ref(), b)))
|
("export" [a, ..b] Self::Export(FileBrowserCommand::from_edn(state, &a.to_ref(), b)))
|
||||||
});
|
});
|
||||||
|
|
@ -44,6 +50,44 @@ edn_command!(PoolClipCommand: |state: MidiPool| {
|
||||||
("set-length" [i: usize, l: usize] Self::SetLength(i.expect("no index"), l.expect("no length")))
|
("set-length" [i: usize, l: usize] Self::SetLength(i.expect("no index"), l.expect("no length")))
|
||||||
("set-color" [i: usize, c: ItemColor] Self::SetColor(i.expect("no index"), c.expect("no color")))
|
("set-color" [i: usize, c: ItemColor] Self::SetColor(i.expect("no index"), c.expect("no color")))
|
||||||
});
|
});
|
||||||
|
#[derive(Clone, Debug, PartialEq)] pub enum ClipRenameCommand {
|
||||||
|
Begin,
|
||||||
|
Cancel,
|
||||||
|
Confirm,
|
||||||
|
Set(Arc<str>),
|
||||||
|
}
|
||||||
|
edn_command!(ClipRenameCommand: |state: MidiPool| {
|
||||||
|
("begin" [] Self::Begin)
|
||||||
|
("cancel" [] Self::Cancel)
|
||||||
|
("confirm" [] Self::Confirm)
|
||||||
|
("set" [n: Arc<str>] Self::Set(n.expect("no name")))
|
||||||
|
});
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)] pub enum ClipLengthCommand {
|
||||||
|
Begin,
|
||||||
|
Cancel,
|
||||||
|
Set(usize),
|
||||||
|
Next,
|
||||||
|
Prev,
|
||||||
|
Inc,
|
||||||
|
Dec,
|
||||||
|
}
|
||||||
|
edn_command!(ClipLengthCommand: |state: MidiPool| {
|
||||||
|
("begin" [] Self::Begin)
|
||||||
|
("cancel" [] Self::Cancel)
|
||||||
|
("next" [] Self::Next)
|
||||||
|
("prev" [] Self::Prev)
|
||||||
|
("inc" [] Self::Inc)
|
||||||
|
("dec" [] Self::Dec)
|
||||||
|
("set" [l: usize] Self::Set(l.expect("no length")))
|
||||||
|
});
|
||||||
|
edn_command!(FileBrowserCommand: |state: MidiPool| {
|
||||||
|
("begin" [] Self::Begin)
|
||||||
|
("cancel" [] Self::Cancel)
|
||||||
|
("confirm" [] Self::Confirm)
|
||||||
|
("select" [i: usize] Self::Select(i.expect("no index")))
|
||||||
|
("chdir" [p: PathBuf] Self::Chdir(p.expect("no path")))
|
||||||
|
("filter" [f: Arc<str>] Self::Filter(f.expect("no filter")))
|
||||||
|
});
|
||||||
impl<T: HasClips> Command<T> for PoolClipCommand {
|
impl<T: HasClips> Command<T> for PoolClipCommand {
|
||||||
fn execute (self, model: &mut T) -> Perhaps<Self> {
|
fn execute (self, model: &mut T) -> Perhaps<Self> {
|
||||||
use PoolClipCommand::*;
|
use PoolClipCommand::*;
|
||||||
|
|
@ -281,16 +325,6 @@ input_to_command!(FileBrowserCommand: |state: MidiPool, input: Event|{
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
pub enum ClipLengthCommand {
|
|
||||||
Begin,
|
|
||||||
Cancel,
|
|
||||||
Set(usize),
|
|
||||||
Next,
|
|
||||||
Prev,
|
|
||||||
Inc,
|
|
||||||
Dec,
|
|
||||||
}
|
|
||||||
command!(|self: ClipLengthCommand,state:MidiPool|{
|
command!(|self: ClipLengthCommand,state:MidiPool|{
|
||||||
use ClipLengthCommand::*;
|
use ClipLengthCommand::*;
|
||||||
use ClipLengthFocus::*;
|
use ClipLengthFocus::*;
|
||||||
|
|
@ -364,13 +398,6 @@ impl InputToCommand<Event, MidiPool> for ClipRenameCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum ClipRenameCommand {
|
|
||||||
Begin,
|
|
||||||
Cancel,
|
|
||||||
Confirm,
|
|
||||||
Set(Arc<str>),
|
|
||||||
}
|
|
||||||
impl Command<MidiPool> for ClipRenameCommand {
|
impl Command<MidiPool> for ClipRenameCommand {
|
||||||
fn execute (self, state: &mut MidiPool) -> Perhaps<Self> {
|
fn execute (self, state: &mut MidiPool) -> Perhaps<Self> {
|
||||||
use ClipRenameCommand::*;
|
use ClipRenameCommand::*;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue