enable adding midi ins and outs

This commit is contained in:
🪞👃🪞 2025-01-25 00:53:39 +01:00
parent 76cefdca61
commit 5d6592bbdf
12 changed files with 171 additions and 165 deletions

View file

@ -70,7 +70,7 @@ pub const fn peek_src <'a> (source: &'a str) -> Option<Token<'a>> {
_ => token.error(Unexpected(c))
},
Sym(_) => match c {
'a'..='z'|'0'..='9'|'-' => token.grow_sym(),
'a'..='z'|'A'..='Z'|'0'..='9'|'-' => token.grow_sym(),
' '|'\n'|'\r'|'\t'|')' => return Some(token),
_ => token.error(Unexpected(c))
},

View file

@ -16,3 +16,13 @@ pub trait Command<S>: Send + Sync + Sized {
Ok(self.execute(state)?.map(wrap))
}
}
impl<S, T: Command<S>> Command<S> for Option<T> {
fn execute (self, _: &mut S) -> Perhaps<Self> {
Ok(None)
}
fn delegate <U> (self, _: &mut S, _: impl Fn(Self)->U) -> Perhaps<U>
where Self: Sized
{
Ok(None)
}
}

View file

@ -100,7 +100,7 @@ impl<'a, C, T: TryFromAtom<'a, C> + Command<C>> AtomCommand<'a, C> for T {}
$(let $arg: Option<$type> = Context::<$type>::get($state, &$arg.value);)?
)*
$(let $rest = iter.clone();)?
return Some($command)
return $command
},)*
_ => None
}
@ -142,7 +142,7 @@ impl<'a, C, T: TryFromAtom<'a, C> + Command<C>> AtomCommand<'a, C> for T {}
$(let $arg: Option<$type> = Context::<$type>::get($state, &$arg.value);)?
)*
$(let $rest = iter.clone();)?
return Some($command)
return $command
}),*
_ => None
}

View file

@ -184,15 +184,15 @@ impl MidiViewer for MidiEditor {
fn set_clip (&mut self, p: Option<&Arc<RwLock<MidiClip>>>) { self.mode.set_clip(p) }
}
atom_command!(MidiEditCommand: |state: MidiEditor| {
("note/append" [] Self::AppendNote)
("note/put" [] Self::PutNote)
("note/del" [] Self::DelNote)
("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")))
("time/lock" [] Self::SetTimeLock(!state.time_lock().get()))
("note/append" [] Some(Self::AppendNote))
("note/put" [] Some(Self::PutNote))
("note/del" [] Some(Self::DelNote))
("note/pos" [a: usize] Some(Self::SetNoteCursor(a.expect("no note cursor"))))
("note/len" [a: usize] Some(Self::SetNoteLength(a.expect("no note length"))))
("time/pos" [a: usize] Some(Self::SetTimeCursor(a.expect("no time cursor"))))
("time/zoom" [a: usize] Some(Self::SetTimeZoom(a.expect("no time zoom"))))
("time/lock" [a: bool] Some(Self::SetTimeLock(a.expect("no time lock"))))
("time/lock" [] Some(Self::SetTimeLock(!state.time_lock().get())))
});
#[derive(Clone, Debug)] pub enum MidiEditCommand {
// TODO: 1-9 seek markers that by default start every 8th of the clip

View file

@ -265,13 +265,13 @@ provide!(ItemColor: |self: MidiPool| {
Clip(PoolClipCommand),
}
atom_command!(PoolCommand: |state: MidiPool| {
("show" [a: bool] Self::Show(a.expect("no flag")))
("select" [i: usize] Self::Select(i.expect("no index")))
("rename" [,..a] ClipRenameCommand::try_from_expr(state, a).map(Self::Rename).expect("invalid command"))
("length" [,..a] ClipLengthCommand::try_from_expr(state, a).map(Self::Length).expect("invalid command"))
("import" [,..a] FileBrowserCommand::try_from_expr(state, a).map(Self::Import).expect("invalid command"))
("export" [,..a] FileBrowserCommand::try_from_expr(state, a).map(Self::Export).expect("invalid command"))
("clip" [,..a] PoolClipCommand::try_from_expr(state, a).map(Self::Clip).expect("invalid command"))
("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::*;
@ -300,14 +300,22 @@ command!(|self: PoolCommand, state: MidiPool|{
SetColor(usize, ItemColor),
}
atom_command!(PoolClipCommand: |state: MidiPool| {
("add" [i: usize, c: MidiClip] Self::Add(i.expect("no index"), c.expect("no clip")))
("delete" [i: usize] Self::Delete(i.expect("no index")))
("swap" [a: usize, b: usize] Self::Swap(a.expect("no index"), b.expect("no index")))
("import" [i: usize, p: PathBuf] Self::Import(i.expect("no index"), p.expect("no path")))
("export" [i: usize, p: PathBuf] Self::Export(i.expect("no index"), p.expect("no path")))
("set-name" [i: usize, n: Arc<str>] Self::SetName(i.expect("no index"), n.expect("no name")))
("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")))
("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> {
@ -381,10 +389,10 @@ impl<T: HasClips> Command<T> for PoolClipCommand {
Set(Arc<str>),
}
atom_command!(ClipRenameCommand: |state: MidiPool| {
("begin" [] Self::Begin)
("cancel" [] Self::Cancel)
("confirm" [] Self::Confirm)
("set" [n: Arc<str>] Self::Set(n.expect("no name")))
("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|{
use ClipRenameCommand::*;
@ -421,13 +429,13 @@ command!(|self: ClipRenameCommand, state: MidiPool|{
Dec,
}
atom_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")))
("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::*;
@ -467,12 +475,12 @@ command!(|self: ClipLengthCommand, state: MidiPool|{
None
});
atom_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")))
("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::*;

View file

@ -662,28 +662,19 @@ pub enum SamplerMode {
Sample(SamplerCommand),
}
atom_command!(SamplerTuiCommand: |state: SamplerTui| {
("select" [i: usize] Self::Select(i.expect("no index")))
("import" [,..a] if let Some(command) = FileBrowserCommand::try_from_expr(state, a) {
Self::Import(command)
} else {
return None
})
("sample" [,..a] if let Some(command) = SamplerCommand::try_from_expr(&state.state, a) {
Self::Sample(command)
} else {
return None
})
});
("select" [i: usize] Some(Self::Select(i.expect("no index"))))
("import" [,..a] FileBrowserCommand::try_from_expr(state, a).map(Self::Import))
("sample" [,..a] SamplerCommand::try_from_expr(&state.state, a).map(Self::Sample)) });
provide!(usize: |self: SamplerTui| {});
provide!(PathBuf: |self: SamplerTui| {});
provide!(Arc<str>: |self: SamplerTui| {});
atom_command!(FileBrowserCommand: |state: SamplerTui| {
("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")))
("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"))))
});
#[derive(Clone, Debug)] pub enum SamplerCommand {
RecordBegin(u7),
@ -697,21 +688,21 @@ atom_command!(FileBrowserCommand: |state: SamplerTui| {
}
atom_command!(SamplerCommand: |state: Sampler| {
("record/begin" [i: u7]
Self::RecordBegin(i.expect("no index")))
Some(Self::RecordBegin(i.expect("no index"))))
("record/cancel" []
Self::RecordCancel)
Some(Self::RecordCancel))
("record/finish" []
Self::RecordFinish)
Some(Self::RecordFinish))
("set/sample" [i: u7, s: Option<Arc<RwLock<Sample>>>]
Self::SetSample(i.expect("no index"), s.expect("no sampler")))
Some(Self::SetSample(i.expect("no index"), s.expect("no sampler"))))
("set/start" [i: u7, s: usize]
Self::SetStart(i.expect("no index"), s.expect("no start")))
Some(Self::SetStart(i.expect("no index"), s.expect("no start"))))
("set/gain" [i: u7, g: f32]
Self::SetGain(i.expect("no index"), g.expect("no garin")))
Some(Self::SetGain(i.expect("no index"), g.expect("no garin"))))
("note/on" [p: u7, v: u7]
Self::NoteOn(p.expect("no pitch"), v.expect("no velocity")))
Some(Self::NoteOn(p.expect("no pitch"), v.expect("no velocity"))))
("note/off" [p: u7]
Self::NoteOff(p.expect("no pitch")))
Some(Self::NoteOff(p.expect("no pitch"))))
});
provide!(u7: |self: Sampler| {});
provide!(Option<Arc<RwLock<Sample>>>: |self: Sampler| {});

View file

@ -7,3 +7,5 @@
(@t select :track 0)
(@tab edit :clip)
(@c color)
(@shift-I input add)
(@shift-O output add)

View file

@ -38,6 +38,8 @@ handle!(TuiIn: |self: Tek, input|Ok({
Editor(MidiEditCommand),
Enqueue(Option<Arc<RwLock<MidiClip>>>),
History(isize),
Input(InputCommand),
Output(OutputCommand),
Pool(PoolCommand),
Sampler(SamplerCommand),
Scene(SceneCommand),
@ -47,34 +49,29 @@ handle!(TuiIn: |self: Tek, input|Ok({
Zoom(Option<usize>),
}
atom_command!(TekCommand: |app: Tek| {
("stop" [] Self::StopAll)
("undo" [d: usize] Self::History(-(d.unwrap_or(0)as isize)))
("redo" [d: usize] Self::History(d.unwrap_or(0) as isize))
("zoom" [z: usize] Self::Zoom(z))
("edit" [] Self::Edit(None))
("edit" [c: bool] Self::Edit(c))
("color" [c: Color] Self::Color(c.map(ItemPalette::from).unwrap_or_default()))
("enqueue" [c: Arc<RwLock<MidiClip>>] Self::Enqueue(c))
("select" [t: usize, s: usize] match (t.expect("no track"), s.expect("no scene")) {
("stop" [] Some(Self::StopAll))
("undo" [d: usize] Some(Self::History(-(d.unwrap_or(0)as isize))))
("redo" [d: usize] Some(Self::History(d.unwrap_or(0) as isize)))
("zoom" [z: usize] Some(Self::Zoom(z)))
("edit" [] Some(Self::Edit(None)))
("edit" [c: bool] Some(Self::Edit(c)))
("color" [c: Color] Some(Self::Color(c.map(ItemPalette::from).unwrap_or_default())))
("enqueue" [c: Arc<RwLock<MidiClip>>] Some(Self::Enqueue(c)))
("clip" [,..a] ClipCommand::try_from_expr(app, a).map(Self::Clip))
("clock" [,..a] ClockCommand::try_from_expr(app.clock(), a).map(Self::Clock))
("editor" [,..a] MidiEditCommand::try_from_expr(app.editor.as_ref().expect("no editor"), a).map(Self::Editor))
("pool" [,..a] PoolCommand::try_from_expr(app.pool.as_ref().expect("no pool"), a).map(Self::Pool))
//("sampler" [,..a] Self::Sampler( //SamplerCommand::try_from_expr(app.sampler().as_ref().expect("no sampler"), a).expect("invalid command")))
("scene" [,..a] SceneCommand::try_from_expr(app, a).map(Self::Scene))
("track" [,..a] TrackCommand::try_from_expr(app, a).map(Self::Track))
("input" [,..a] InputCommand::try_from_expr(app, a).map(Self::Input))
("output" [,..a] OutputCommand::try_from_expr(app, a).map(Self::Output))
("select" [t: usize, s: usize] Some(match (t.expect("no track"), s.expect("no scene")) {
(0, 0) => Self::Select(Selection::Mix),
(t, 0) => Self::Select(Selection::Track(t)),
(0, s) => Self::Select(Selection::Scene(s)),
(t, s) => Self::Select(Selection::Clip(t, s)),
})
("clip" [,..a] Self::Clip(
ClipCommand::try_from_expr(app, a).expect("invalid command: {a:?}")))
("clock" [,..a] Self::Clock(
ClockCommand::try_from_expr(app.clock(), a).expect("invalid command")))
("editor" [,..a] Self::Editor(
MidiEditCommand::try_from_expr(app.editor.as_ref().expect("no editor"), a).expect("invalid command")))
("pool" [,..a] Self::Pool(
PoolCommand::try_from_expr(app.pool.as_ref().expect("no pool"), a).expect("invalid command")))
//("sampler" [,..a] Self::Sampler(
//SamplerCommand::try_from_expr(app.sampler().as_ref().expect("no sampler"), a).expect("invalid command")))
("scene" [,..a] Self::Scene(
SceneCommand::try_from_expr(app, a).expect("invalid command")))
("track" [,..a] Self::Track(
TrackCommand::try_from_expr(app, a).expect("invalid command")))
}))
});
command!(|self: TekCommand, app: Tek|match self {
Self::Zoom(_) => { println!("\n\rtodo: global zoom"); None },
@ -130,6 +127,8 @@ command!(|self: TekCommand, app: Tek|match self {
Self::Clock(cmd) => cmd.delegate(app, Self::Clock)?,
Self::Scene(cmd) => cmd.delegate(app, Self::Scene)?,
Self::Track(cmd) => cmd.delegate(app, Self::Track)?,
Self::Input(cmd) => cmd.delegate(app, Self::Input)?,
Self::Output(cmd) => cmd.delegate(app, Self::Output)?,
Self::Clip(cmd) => cmd.delegate(app, Self::Clip)?,
Self::Editor(cmd) => app.editor.as_mut()
.map(|editor|cmd.delegate(editor, Self::Editor)).transpose()?.flatten(),
@ -192,6 +191,26 @@ command!(|self: TekCommand, app: Tek|match self {
},
_ => todo!("{self:?}")
});
#[derive(Clone, Debug)] pub enum InputCommand { Add }
atom_command!(InputCommand: |app: Tek| {
("add" [] Some(Self::Add))
});
command!(|self: InputCommand, app: Tek|match self {
Self::Add => {
app.midi_ins.push(JackMidiIn::new(&app.jack, &format!("M/{}", app.midi_ins.len()), &[])?);
None
},
});
#[derive(Clone, Debug)] pub enum OutputCommand { Add }
atom_command!(OutputCommand: |app: Tek| {
("add" [] Some(Self::Add))
});
command!(|self: OutputCommand, app: Tek|match self {
Self::Add => {
app.midi_outs.push(JackMidiOut::new(&app.jack, &format!("{}/M", app.midi_outs.len()), &[])?);
None
},
});
#[derive(Clone, Debug)] pub enum TrackCommand {
Add,
Del(usize),
@ -206,17 +225,17 @@ command!(|self: TekCommand, app: Tek|match self {
ToggleMonitor,
}
atom_command!(TrackCommand: |app: Tek| {
("add" [] Self::Add)
("size" [a: usize] Self::SetSize(a.unwrap()))
("zoom" [a: usize] Self::SetZoom(a.unwrap()))
("color" [a: usize] Self::SetColor(a.unwrap().saturating_sub(1), ItemPalette::random()))
("del" [a: usize] Self::Del(a.unwrap().saturating_sub(1)))
("stop" [a: usize] Self::Stop(a.unwrap().saturating_sub(1)))
("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap()))
("play" [] Self::TogglePlay)
("solo" [] Self::ToggleSolo)
("rec" [] Self::ToggleRecord)
("mon" [] Self::ToggleMonitor)
("add" [] Some(Self::Add))
("size" [a: usize] Some(Self::SetSize(a.unwrap())))
("zoom" [a: usize] Some(Self::SetZoom(a.unwrap())))
("color" [a: usize] Some(Self::SetColor(a.unwrap().saturating_sub(1), ItemPalette::random())))
("del" [a: usize] Some(Self::Del(a.unwrap().saturating_sub(1))))
("stop" [a: usize] Some(Self::Stop(a.unwrap().saturating_sub(1))))
("swap" [a: usize, b: usize] Some(Self::Swap(a.unwrap(), b.unwrap())))
("play" [] Some(Self::TogglePlay))
("solo" [] Some(Self::ToggleSolo))
("rec" [] Some(Self::ToggleRecord))
("mon" [] Some(Self::ToggleMonitor))
});
command!(|self: TrackCommand, app: Tek|match self {
Self::Add => {
@ -266,12 +285,12 @@ command!(|self: TrackCommand, app: Tek|match self {
Enqueue(usize),
}
atom_command!(SceneCommand: |app: Tek| {
("add" [] Self::Add)
("del" [a: usize] Self::Del(0))
("zoom" [a: usize] Self::SetZoom(a.unwrap()))
("color" [a: usize] Self::SetColor(a.unwrap().saturating_sub(1), ItemPalette::random()))
("enqueue" [a: usize] Self::Enqueue(a.unwrap().saturating_sub(1)))
("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap()))
("add" [] Some(Self::Add))
("del" [a: usize] Some(Self::Del(0)))
("zoom" [a: usize] Some(Self::SetZoom(a.unwrap())))
("color" [a: usize] Some(Self::SetColor(a.unwrap().saturating_sub(1), ItemPalette::random())))
("enqueue" [a: usize] Some(Self::Enqueue(a.unwrap().saturating_sub(1))))
("swap" [a: usize, b: usize] Some(Self::Swap(a.unwrap(), b.unwrap())))
});
command!(|self: SceneCommand, app: Tek|match self {
Self::Add => {
@ -307,18 +326,12 @@ command!(|self: SceneCommand, app: Tek|match self {
SetColor(usize, usize, ItemPalette),
}
atom_command!(ClipCommand: |app: Tek| {
("get" [a: usize ,b: usize]
Self::Get(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1)))
("put" [a: usize, b: usize, c: Option<Arc<RwLock<MidiClip>>>]
Self::Put(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1), c.unwrap()))
("enqueue" [a: usize, b: usize]
Self::Enqueue(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1)))
("edit" [a: Option<Arc<RwLock<MidiClip>>>]
Self::Edit(a.unwrap()))
("loop" [a: usize, b: usize, c: bool]
Self::SetLoop(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1), c.unwrap()))
("color" [a: usize, b: usize]
Self::SetColor(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1), ItemPalette::random()))
("get" [a: usize ,b: usize] Some(Self::Get(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1))))
("put" [a: usize, b: usize, c: Option<Arc<RwLock<MidiClip>>>] Some(Self::Put(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1), c.unwrap())))
("enqueue" [a: usize, b: usize] Some(Self::Enqueue(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1))))
("edit" [a: Option<Arc<RwLock<MidiClip>>>] Some(Self::Edit(a.unwrap())))
("loop" [a: usize, b: usize, c: bool] Some(Self::SetLoop(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1), c.unwrap())))
("color" [a: usize, b: usize] Some(Self::SetColor(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1), ItemPalette::random())))
});
command!(|self: ClipCommand, app: Tek|match self {
Self::Get(track, scene) => { todo!() },

View file

@ -128,15 +128,6 @@ impl Tek {
name,
..Default::default()
};
//let midi_in = JackMidiIn::new(&self.jack, &format!("{}I", &track.name), midi_from)?;
//midi_in.connect_to_matching()?;
//track.player.midi_ins.push(midi_in);
//let midi_out = JackMidiOut::new(&self.jack, &format!("{}O", &track.name), midi_to)?;
//midi_out.connect_to_matching()?;
//track.player.midi_outs.push(midi_out);
self.tracks_mut().push(track);
let len = self.tracks().len();
let index = len - 1;

View file

@ -197,15 +197,10 @@ impl Tek {
let conn = move|conn: &PortConnect|{
Fill::x(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, conn.info()))))
};
let header: ThunkBox<_> = io_header!(
self,
" I ",
" midi ins",
self.midi_ins.len(),
self.midi_ins().get(0).map(move|input: &JackMidiIn|Bsp::s(
let header: ThunkBox<_> = io_header!(self, " I ", " midi ins", self.midi_ins.len(),
Map::new(||self.midi_ins.iter(), move|input, index|map_south(index as u16, 1u16, Bsp::s(
Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(input.name())))),
input.conn().get(0).map(conn)
)));
input.conn().get(0).map(conn)))));
let cells: ThunkBox<_> = per_track!(self.size.w();|self, track, t|{
let rec = track.player.recording;
let mon = track.player.monitoring;
@ -229,15 +224,11 @@ impl Tek {
let bg = Tui::g(64);
let mut h = 2.max(1 + self.midi_outs.len());
for midi_out in self.midi_outs.iter() { h += midi_out.conn().len() }
let header: ThunkBox<_> = io_header!(
self,
" O ",
" midi outs",
self.midi_outs.len(),
self.midi_outs().get(0).map(move|output: &JackMidiOut|Bsp::s(
let header: ThunkBox<_> = io_header!(self, " O ", " midi outs", self.midi_outs.len(),
Map::new(||self.midi_outs.iter(), move|output, index|map_south(index as u16, 1u16, Bsp::s(
Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(output.name())))),
output.conn().get(0).map(|connect|Fill::x(Align::w(Tui::bold(false,
Tui::fg_bg(fg, bg, connect.info()))))))));
Tui::fg_bg(fg, bg, connect.info())))))))));
let mute = false;
let solo = false;
let cells: ThunkBox<_> = per_track!(self.size.w();|self, track, t|{

View file

@ -25,18 +25,18 @@ pub enum ClockCommand {
provide_num!(u32: |self: Clock| {});
provide!(f64: |self: Clock| {});
atom_command!(ClockCommand: |state: Clock| {
("play" [] Self::Play(None))
("play" [t: u32] Self::Play(t))
("pause" [] Self::Pause(None))
("pause" [t: u32] Self::Pause(t))
("toggle" [] if state.is_rolling() { Self::Pause(None) } else { Self::Play(None) })
("toggle" [t: u32] if state.is_rolling() { Self::Pause(t) } else { Self::Play(t) })
("seek/usec" [t: f64] Self::SeekUsec(t.expect("no usec")))
("seek/pulse" [t: f64] Self::SeekPulse(t.expect("no pulse")))
("seek/sample" [t: f64] Self::SeekSample(t.expect("no sample")))
("set/bpm" [t: f64] Self::SetBpm(t.expect("no bpm")))
("set/sync" [t: f64] Self::SetSync(t.expect("no sync")))
("set/quant" [t: f64] Self::SetQuant(t.expect("no quant")))
("play" [] Some(Self::Play(None)))
("play" [t: u32] Some(Self::Play(t)))
("pause" [] Some(Self::Pause(None)))
("pause" [t: u32] Some(Self::Pause(t)))
("toggle" [] Some(if state.is_rolling() { Self::Pause(None) } else { Self::Play(None) }))
("toggle" [t: u32] Some(if state.is_rolling() { Self::Pause(t) } else { Self::Play(t) }))
("seek/usec" [t: f64] Some(Self::SeekUsec(t.expect("no usec"))))
("seek/pulse" [t: f64] Some(Self::SeekPulse(t.expect("no pulse"))))
("seek/sample" [t: f64] Some(Self::SeekSample(t.expect("no sample"))))
("set/bpm" [t: f64] Some(Self::SetBpm(t.expect("no bpm"))))
("set/sync" [t: f64] Some(Self::SetSync(t.expect("no sync"))))
("set/quant" [t: f64] Some(Self::SetQuant(t.expect("no quant"))))
});
impl<T: HasClock> Command<T> for ClockCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {

View file

@ -21,8 +21,8 @@ handle!(TuiIn: |self: Example, input|{
});
enum ExampleCommand { Next, Previous }
atom_command!(ExampleCommand: |app: Example| {
(":prev" [] Self::Previous)
(":next" [] Self::Next)
(":prev" [] Some(Self::Previous))
(":next" [] Some(Self::Next))
});
command!(|self: ExampleCommand, state: Example|match self {
Self::Next =>