mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
re-enabled space = play! but not pause
This commit is contained in:
parent
0fb7655b53
commit
0ce0a07713
6 changed files with 110 additions and 41 deletions
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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 [
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue