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),* $(,)? }) => {
|
(# $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> {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,49 @@ pub trait EdnCommand<C>: Command<C> {
|
||||||
|
|
||||||
/** Implement `EdnCommand` for given `State` and `Command` */
|
/** Implement `EdnCommand` for given `State` and `Command` */
|
||||||
#[macro_export] macro_rules! edn_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| { $((
|
($Command:ty : |$state:ident:$State:ty| { $((
|
||||||
// identifier
|
// identifier
|
||||||
$key:literal [
|
$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.play_clip = Some((Moment::zero(&clock.timebase), Some(clip.clone())));
|
||||||
model
|
model
|
||||||
});
|
});
|
||||||
has_clock!(|self: MidiPlayer|&self.clock);
|
has_clock!(|self: MidiPlayer|self.clock);
|
||||||
impl HasMidiIns for MidiPlayer {
|
impl HasMidiIns for MidiPlayer {
|
||||||
fn midi_ins (&self) -> &Vec<JackPort<MidiIn>> { &self.midi_ins }
|
fn midi_ins (&self) -> &Vec<JackPort<MidiIn>> { &self.midi_ins }
|
||||||
fn midi_ins_mut (&mut self) -> &mut Vec<JackPort<MidiIn>> { &mut self.midi_ins }
|
fn midi_ins_mut (&mut self) -> &mut Vec<JackPort<MidiIn>> { &mut self.midi_ins }
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
(@u undo 1)
|
(@u undo 1)
|
||||||
(@shift-u redo 1)
|
(@shift-u redo 1)
|
||||||
(@space play/toggle)
|
(@space clock play 0)
|
||||||
(@shift-space play/start-toggle)
|
(@shift-space clock play 0)
|
||||||
(@e editor show :pool-clip)
|
(@e editor show :pool-clip)
|
||||||
(@ctrl-a scene add)
|
(@ctrl-a scene add)
|
||||||
(@ctrl-t track add)
|
(@ctrl-t track add)
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ pub use ::tek_tui::{
|
||||||
pub history: Vec<AppCommand>,
|
pub history: Vec<AppCommand>,
|
||||||
}
|
}
|
||||||
has_size!(<TuiOut>|self: App|&self.size);
|
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_clips!(|self: App|self.pool.as_ref().expect("no clip pool").clips);
|
||||||
//has_editor!(|self: App|self.editor.as_ref().expect("no editor"));
|
//has_editor!(|self: App|self.editor.as_ref().expect("no editor"));
|
||||||
has_jack!(|self: App|&self.jack);
|
has_jack!(|self: App|&self.jack);
|
||||||
|
|
@ -427,33 +427,33 @@ handle!(TuiIn: |self: App, input|Ok({
|
||||||
}
|
}
|
||||||
edn_command!(AppCommand: |state: App| {
|
edn_command!(AppCommand: |state: App| {
|
||||||
("stop-all" [] Self::StopAll)
|
("stop-all" [] Self::StopAll)
|
||||||
("compact" [c: bool] Self::Compact(c))
|
("compact" [c: bool] Self::Compact(c))
|
||||||
("color" [c: Color] Self::Color(c.map(ItemPalette::from).unwrap_or_default()))
|
("color" [c: Color] Self::Color(c.map(ItemPalette::from).unwrap_or_default()))
|
||||||
("undo" [d: usize] Self::History(-(d.unwrap_or(0)as isize)))
|
("undo" [d: usize] Self::History(-(d.unwrap_or(0)as isize)))
|
||||||
("redo" [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))
|
("zoom" [z: usize] Self::Zoom(z))
|
||||||
("select" [t: usize, s: usize] match (t.expect("no track"), s.expect("no scene")) {
|
("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),
|
(0, 0) => Self::Select(Selection::Mix),
|
||||||
(t, 0) => Self::Select(Selection::Track(t - 1)),
|
(t, 0) => Self::Select(Selection::Track(t - 1)),
|
||||||
(0, s) => Self::Select(Selection::Scene(s - 1)),
|
(0, s) => Self::Select(Selection::Scene(s - 1)),
|
||||||
(t, s) => Self::Select(Selection::Clip(t - 1, 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(
|
("clip" [a, ..b] Self::Clip(ClipCommand::from_edn(state, &a.to_ref(), b)
|
||||||
ClipCommand::from_edn(state, &a.to_ref(), b).expect("invalid command")))
|
.expect("invalid command")))
|
||||||
("clock" [a, ..b] Self::Clock(
|
("clock" [a, ..b] Self::Clock(ClockCommand::from_edn(state.clock(), &a.to_ref(), b)
|
||||||
ClockCommand::from_edn(state, &a.to_ref(), b).expect("invalid command")))
|
.expect("invalid command")))
|
||||||
("editor" [a, ..b] Self::Editor(
|
("editor" [a, ..b] Self::Editor(MidiEditCommand::from_edn(state.editor.as_ref().expect("no editor"), &a.to_ref(), b)
|
||||||
MidiEditCommand::from_edn(state.editor.as_ref().expect("no editor"), &a.to_ref(), b).expect("invalid command")))
|
.expect("invalid command")))
|
||||||
("pool" [a, ..b] Self::Pool(
|
("pool" [a, ..b] Self::Pool(PoolCommand::from_edn(state.pool.as_ref().expect("no pool"), &a.to_ref(), b)
|
||||||
PoolCommand::from_edn(state.pool.as_ref().expect("no pool"), &a.to_ref(), b).expect("invalid command")))
|
.expect("invalid command")))
|
||||||
("sampler" [a, ..b] Self::Sampler(
|
("sampler" [a, ..b] Self::Sampler(SamplerCommand::from_edn(state.sampler.as_ref().expect("no sampler"), &a.to_ref(), b)
|
||||||
SamplerCommand::from_edn(state.sampler.as_ref().expect("no sampler"), &a.to_ref(), b).expect("invalid command")))
|
.expect("invalid command")))
|
||||||
("scene" [a, ..b] Self::Scene(
|
("scene" [a, ..b] Self::Scene(SceneCommand::from_edn(state, &a.to_ref(), b)
|
||||||
SceneCommand::from_edn(state, &a.to_ref(), b).expect("invalid command")))
|
.expect("invalid command")))
|
||||||
("track" [a, ..b] Self::Track(
|
("track" [a, ..b] Self::Track(TrackCommand::from_edn(state, &a.to_ref(), b)
|
||||||
TrackCommand::from_edn(state, &a.to_ref(), b).expect("invalid command")))
|
.expect("invalid command")))
|
||||||
});
|
});
|
||||||
command!(|self: AppCommand, state: App|match self {
|
command!(|self: AppCommand, state: App|match self {
|
||||||
Self::Zoom(_) => { println!("\n\rtodo: global zoom"); None },
|
Self::Zoom(_) => { println!("\n\rtodo: global zoom"); None },
|
||||||
|
|
@ -574,7 +574,7 @@ pub trait HasSelection {
|
||||||
/// Device chain
|
/// Device chain
|
||||||
devices: Vec<Box<dyn Device>>,
|
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);
|
has_player!(|self: Track|self.player);
|
||||||
impl Track {
|
impl Track {
|
||||||
const MIN_WIDTH: usize = 9;
|
const MIN_WIDTH: usize = 9;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
pub trait HasClock: Send + Sync {
|
pub trait HasClock: Send + Sync {
|
||||||
fn clock (&self) -> &Clock;
|
fn clock (&self) -> &Clock;
|
||||||
|
fn clock_mut (&mut self) -> &mut Clock;
|
||||||
}
|
}
|
||||||
#[macro_export] macro_rules! has_clock {
|
#[macro_export] macro_rules! has_clock {
|
||||||
(|$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)?),*>)? HasClock for $Struct $(<$($L),*$($T),*>)? {
|
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),
|
SetQuant(f64),
|
||||||
SetSync(f64),
|
SetSync(f64),
|
||||||
}
|
}
|
||||||
impl<T: HasClock> EdnCommand<T> for ClockCommand {
|
edn_provide!(# u32: |self: Clock| {});
|
||||||
fn from_edn <'a> (state: &T, head: &EdnItem<&str>, tail: &'a [EdnItem<&str>]) -> Option<Self> {
|
edn_provide!(f64: |self: Clock| {});
|
||||||
todo!()
|
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 {
|
impl<T: HasClock> Command<T> for ClockCommand {
|
||||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
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::*;
|
use ClockCommand::*;
|
||||||
match self {
|
match self {
|
||||||
Play(start) => state.clock().play_from(start)?,
|
Play(start) => state.play_from(start)?,
|
||||||
Pause(pause) => state.clock().pause_at(pause)?,
|
Pause(pause) => state.pause_at(pause)?,
|
||||||
SeekUsec(usec) => state.clock().playhead.update_from_usec(usec),
|
SeekUsec(usec) => state.playhead.update_from_usec(usec),
|
||||||
SeekSample(sample) => state.clock().playhead.update_from_sample(sample),
|
SeekSample(sample) => state.playhead.update_from_sample(sample),
|
||||||
SeekPulse(pulse) => state.clock().playhead.update_from_pulse(pulse),
|
SeekPulse(pulse) => state.playhead.update_from_pulse(pulse),
|
||||||
SetBpm(bpm) => return Ok(Some(SetBpm(state.clock().timebase().bpm.set(bpm)))),
|
SetBpm(bpm) => return Ok(Some(SetBpm(state.timebase().bpm.set(bpm)))),
|
||||||
SetQuant(quant) => return Ok(Some(SetQuant(state.clock().quant.set(quant)))),
|
SetQuant(quant) => return Ok(Some(SetQuant(state.quant.set(quant)))),
|
||||||
SetSync(sync) => return Ok(Some(SetSync(state.clock().sync.set(sync)))),
|
SetSync(sync) => return Ok(Some(SetSync(state.sync.set(sync)))),
|
||||||
};
|
};
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue