re-enabled space = play! but not pause

This commit is contained in:
🪞👃🪞 2025-01-14 22:25:18 +01:00
parent 0fb7655b53
commit 0ce0a07713
6 changed files with 110 additions and 41 deletions

View file

@ -17,7 +17,19 @@ use crate::*;
}
}
};
// Provide a value that may also be a numeric literal in the EDN
// Provide a value that may also be a numeric literal in the EDN, to a generic implementation.
(# $type:ty:|$self:ident:<$T:ident:$Trait:path>|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<'a, $T: $Trait> EdnProvide<'a, $type> for $T {
fn get <S: AsRef<str>> (&'a $self, edn: &'a EdnItem<S>) -> Option<$type> {
Some(match edn.to_ref() {
$(EdnItem::Sym($pat) => $expr,)*
EdnItem::Num(n) => n as $type,
_ => return None
})
}
}
};
// Provide a value that may also be a numeric literal in the EDN, to a concrete implementation.
(# $type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<'a> EdnProvide<'a, $type> for $State {
fn get <S: AsRef<str>> (&'a $self, edn: &'a EdnItem<S>) -> Option<$type> {

View file

@ -15,6 +15,49 @@ pub trait EdnCommand<C>: Command<C> {
/** Implement `EdnCommand` for given `State` and `Command` */
#[macro_export] macro_rules! edn_command {
($Command:ty : |$state:ident:<$T:ident: $Trait:path>| { $((
// identifier
$key:literal [
// named parameters
$(
// argument name
$arg:ident
// if type is not provided defaults to EdnItem
$(
// type:name separator
:
// argument type
$type:ty
)?
),*
// rest of parameters
$(, ..$rest:ident)?
]
// bound command:
$command:expr
))* }) => {
impl<$T: $Trait> EdnCommand<$T> for $Command {
fn from_edn <'a> (
$state: &$T,
head: &EdnItem<&str>,
tail: &'a [EdnItem<&'a str>]
) -> Option<Self> {
$(if let (EdnItem::Key($key), [ // if the identifier matches
// bind argument ids
$($arg),*
// bind rest parameters
$(, $rest @ ..)?
]) = (head, tail) {
$(
$(let $arg: Option<$type> = EdnProvide::<$type>::get($state, $arg);)?
)*
//$(edn_command!(@bind $state => $arg $(?)? : $type);)*
return Some($command)
})*
None
}
}
};
($Command:ty : |$state:ident:$State:ty| { $((
// identifier
$key:literal [

View file

@ -108,7 +108,7 @@ from!(|state: (&Clock, &Arc<RwLock<MidiClip>>)|MidiPlayer = {
model.play_clip = Some((Moment::zero(&clock.timebase), Some(clip.clone())));
model
});
has_clock!(|self: MidiPlayer|&self.clock);
has_clock!(|self: MidiPlayer|self.clock);
impl HasMidiIns for MidiPlayer {
fn midi_ins (&self) -> &Vec<JackPort<MidiIn>> { &self.midi_ins }
fn midi_ins_mut (&mut self) -> &mut Vec<JackPort<MidiIn>> { &mut self.midi_ins }

View file

@ -1,7 +1,7 @@
(@u undo 1)
(@shift-u redo 1)
(@space play/toggle)
(@shift-space play/start-toggle)
(@space clock play 0)
(@shift-space clock play 0)
(@e editor show :pool-clip)
(@ctrl-a scene add)
(@ctrl-t track add)

View file

@ -48,7 +48,7 @@ pub use ::tek_tui::{
pub history: Vec<AppCommand>,
}
has_size!(<TuiOut>|self: App|&self.size);
has_clock!(|self: App|&self.clock);
has_clock!(|self: App|self.clock);
has_clips!(|self: App|self.pool.as_ref().expect("no clip pool").clips);
//has_editor!(|self: App|self.editor.as_ref().expect("no editor"));
has_jack!(|self: App|&self.jack);
@ -432,28 +432,28 @@ edn_command!(AppCommand: |state: App| {
("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))
("enqueue" [c: Arc<RwLock<MidiClip>>] Self::Enqueue(c))
("select" [t: usize, s: usize] match (t.expect("no track"), s.expect("no scene")) {
(0, 0) => Self::Select(Selection::Mix),
(t, 0) => Self::Select(Selection::Track(t - 1)),
(0, s) => Self::Select(Selection::Scene(s - 1)),
(t, s) => Self::Select(Selection::Clip(t - 1, s - 1)),
})
("enqueue" [c: Arc<RwLock<MidiClip>>] Self::Enqueue(c))
("clip" [a, ..b] Self::Clip(
ClipCommand::from_edn(state, &a.to_ref(), b).expect("invalid command")))
("clock" [a, ..b] Self::Clock(
ClockCommand::from_edn(state, &a.to_ref(), b).expect("invalid command")))
("editor" [a, ..b] Self::Editor(
MidiEditCommand::from_edn(state.editor.as_ref().expect("no editor"), &a.to_ref(), b).expect("invalid command")))
("pool" [a, ..b] Self::Pool(
PoolCommand::from_edn(state.pool.as_ref().expect("no pool"), &a.to_ref(), b).expect("invalid command")))
("sampler" [a, ..b] Self::Sampler(
SamplerCommand::from_edn(state.sampler.as_ref().expect("no sampler"), &a.to_ref(), b).expect("invalid command")))
("scene" [a, ..b] Self::Scene(
SceneCommand::from_edn(state, &a.to_ref(), b).expect("invalid command")))
("track" [a, ..b] Self::Track(
TrackCommand::from_edn(state, &a.to_ref(), b).expect("invalid command")))
("clip" [a, ..b] Self::Clip(ClipCommand::from_edn(state, &a.to_ref(), b)
.expect("invalid command")))
("clock" [a, ..b] Self::Clock(ClockCommand::from_edn(state.clock(), &a.to_ref(), b)
.expect("invalid command")))
("editor" [a, ..b] Self::Editor(MidiEditCommand::from_edn(state.editor.as_ref().expect("no editor"), &a.to_ref(), b)
.expect("invalid command")))
("pool" [a, ..b] Self::Pool(PoolCommand::from_edn(state.pool.as_ref().expect("no pool"), &a.to_ref(), b)
.expect("invalid command")))
("sampler" [a, ..b] Self::Sampler(SamplerCommand::from_edn(state.sampler.as_ref().expect("no sampler"), &a.to_ref(), b)
.expect("invalid command")))
("scene" [a, ..b] Self::Scene(SceneCommand::from_edn(state, &a.to_ref(), b)
.expect("invalid command")))
("track" [a, ..b] Self::Track(TrackCommand::from_edn(state, &a.to_ref(), b)
.expect("invalid command")))
});
command!(|self: AppCommand, state: App|match self {
Self::Zoom(_) => { println!("\n\rtodo: global zoom"); None },
@ -574,7 +574,7 @@ pub trait HasSelection {
/// Device chain
devices: Vec<Box<dyn Device>>,
}
has_clock!(|self: Track|self.player.clock());
has_clock!(|self: Track|self.player.clock);
has_player!(|self: Track|self.player);
impl Track {
const MIN_WIDTH: usize = 9;

View file

@ -1,11 +1,13 @@
use crate::*;
pub trait HasClock: Send + Sync {
fn clock (&self) -> &Clock;
fn clock_mut (&mut self) -> &mut Clock;
}
#[macro_export] macro_rules! has_clock {
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
impl $(<$($L),*$($T $(: $U)?),*>)? HasClock for $Struct $(<$($L),*$($T),*>)? {
fn clock (&$self) -> &Clock { $cb }
fn clock (&$self) -> &Clock { &$cb }
fn clock_mut (&mut $self) -> &mut Clock { &mut $cb }
}
}
}
@ -20,23 +22,35 @@ pub enum ClockCommand {
SetQuant(f64),
SetSync(f64),
}
impl<T: HasClock> EdnCommand<T> for ClockCommand {
fn from_edn <'a> (state: &T, head: &EdnItem<&str>, tail: &'a [EdnItem<&str>]) -> Option<Self> {
todo!()
}
}
edn_provide!(# u32: |self: Clock| {});
edn_provide!(f64: |self: Clock| {});
edn_command!(ClockCommand: |state: Clock| {
("play" [t: u32] Self::Play(t))
("pause" [t: u32] Self::Pause(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")))
});
impl<T: HasClock> Command<T> for ClockCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
self.execute(state.clock_mut())
}
}
impl Command<Clock> for ClockCommand {
fn execute (self, state: &mut Clock) -> Perhaps<Self> {
use ClockCommand::*;
match self {
Play(start) => state.clock().play_from(start)?,
Pause(pause) => state.clock().pause_at(pause)?,
SeekUsec(usec) => state.clock().playhead.update_from_usec(usec),
SeekSample(sample) => state.clock().playhead.update_from_sample(sample),
SeekPulse(pulse) => state.clock().playhead.update_from_pulse(pulse),
SetBpm(bpm) => return Ok(Some(SetBpm(state.clock().timebase().bpm.set(bpm)))),
SetQuant(quant) => return Ok(Some(SetQuant(state.clock().quant.set(quant)))),
SetSync(sync) => return Ok(Some(SetSync(state.clock().sync.set(sync)))),
Play(start) => state.play_from(start)?,
Pause(pause) => state.pause_at(pause)?,
SeekUsec(usec) => state.playhead.update_from_usec(usec),
SeekSample(sample) => state.playhead.update_from_sample(sample),
SeekPulse(pulse) => state.playhead.update_from_pulse(pulse),
SetBpm(bpm) => return Ok(Some(SetBpm(state.timebase().bpm.set(bpm)))),
SetQuant(quant) => return Ok(Some(SetQuant(state.quant.set(quant)))),
SetSync(sync) => return Ok(Some(SetSync(state.sync.set(sync)))),
};
Ok(None)
}