diff --git a/src/device/arrange.rs b/src/device/arrange.rs index fa481c92..3b19c891 100644 --- a/src/device/arrange.rs +++ b/src/device/arrange.rs @@ -46,40 +46,83 @@ pub struct Arrangement { #[cfg(feature = "scene")] pub scene_scroll: usize, } -def_command!(TrackCommand: |track: Track| { - Stop => { track.sequencer.enqueue_next(None); Ok(None) }, - SetRec { rec: Option } => toggle_bool(&mut track.sequencer.recording, rec, |rec|Self::SetRec { rec }), - SetMon { mon: Option } => toggle_bool(&mut track.sequencer.monitoring, mon, |mon|Self::SetMon { mon }), - SetMute { mute: Option } => todo!(), - SetSolo { solo: Option } => todo!(), - SetSize { size: usize } => todo!(), - SetZoom { zoom: usize } => todo!(), - SetName { name: Arc } => swap_value(&mut track.name, name, |name|Self::SetName { name }), - SetColor { color: ItemTheme } => swap_value(&mut track.color, color, |color|Self::SetColor { color }), -}); +#[tek_proc::commands(TrackCommand = "track")] +impl Track { + #[command(Stop = "stop")] + fn stop (&mut self) -> Perhaps { + self.sequencer.enqueue_next(None); + Ok(None) + } + #[command(SetRec = "rec")] + fn set_rec (&mut self, rec: Option) -> Perhaps { + toggle_bool(&mut self.sequencer.recording, &rec, |rec|TrackCommand::SetRec { rec }) + } + #[command(SetMon = "mon")] + fn set_mon (&mut self, mon: Option) -> Perhaps { + toggle_bool(&mut self.sequencer.monitoring, &mon, |mon|TrackCommand::SetMon { mon }) + } + #[command(SetMute = "mute")] + fn set_mute (&mut self, mute: Option) -> Perhaps { + todo!() + } + #[command(SetSolo = "solo")] + fn set_solo (&mut self, solo: Option) -> Perhaps { + todo!() + } + #[command(SetSize = "size")] + fn set_size (&mut self, size: usize) -> Perhaps { + todo!() + } + #[command(SetZoom = "zoom")] + fn set_zoom (&mut self, zoom: usize) -> Perhaps { + todo!() + } + #[command(SetName = "name")] + fn set_name (&mut self, name: Arc) -> Perhaps { + swap_value(&mut self.name, &name, |name|TrackCommand::SetName { name }) + } + #[command(SetColor = "color")] + fn set_color (&mut self, color: ItemTheme) -> Perhaps { + swap_value(&mut self.color, &color, |color|TrackCommand::SetColor { color }) + } +} -def_command!(SceneCommand: |scene: Scene| { - SetSize { size: usize } => { todo!() }, - SetZoom { size: usize } => { todo!() }, - SetName { name: Arc } => - swap_value(&mut scene.name, name, |name|Self::SetName{name}), - SetColor { color: ItemTheme } => - swap_value(&mut scene.color, color, |color|Self::SetColor{color}), -}); +#[tek_proc::commands(SceneCommand = "scene")] +impl Scene { + #[command(SetSize = "size")] + fn set_size (&mut self, size: usize) -> Perhaps { + todo!() + } + #[command(SetZoom = "zoom")] + fn set_zoom (&mut self, size: usize) -> Perhaps { + todo!() + } + #[command(SetName = "name")] + fn set_name (&mut self, name: Arc) -> Perhaps { + swap_value(&mut self.name, &name, |name|SceneCommand::SetName{name}) + } + #[command(SetColor = "color")] + fn set_color (&mut self, color: ItemTheme) -> Perhaps { + swap_value(&mut self.color, &color, |color|SceneCommand::SetColor{color}) + } +} -def_command!(ClipCommand: |clip: MidiClip| { - SetColor { color: Option } => { +#[tek_proc::commands(ClipCommand = "clip")] +impl MidiClip { + #[command(SetColor = "color")] + fn set_color (&mut self, color: Option) -> Perhaps { //(SetColor [t: usize, s: usize, c: ItemTheme] //clip.clip_set_color(t, s, c).map(|o|Self::SetColor(t, s, o))))); //("color" [a: usize, b: usize] Some(Self::SetColor(a.unwrap(), b.unwrap(), ItemTheme::random()))) todo!() - }, - SetLoop { looping: Option } => { + } + #[command(SetLoop = "loop")] + fn set_loop (&mut self, looping: Option) -> Perhaps { //(SetLoop [t: usize, s: usize, l: bool] cmd_todo!("\n\rtodo: {self:?}")) //("loop" [a: usize, b: usize, c: bool] Some(Self::SetLoop(a.unwrap(), b.unwrap(), c.unwrap()))) todo!() } -}); +} /// Represents the current user selection in the arranger #[derive(PartialEq, Clone, Copy, Debug, Default)] @@ -1068,3 +1111,27 @@ macro_rules! def_sizes_iter { def_sizes_iter!(PortsSizes => Arc, [Connect]); def_sizes_iter!(ScenesSizes => Scene); def_sizes_iter!(TracksSizes => Track); + +fn swap_value ( + target: &mut T, value: &T, returned: impl Fn(T)->U +) -> Perhaps { + if *target == *value { + Ok(None) + } else { + let mut value = value.clone(); + std::mem::swap(target, &mut value); + Ok(Some(returned(value))) + } +} + +fn toggle_bool ( + target: &mut bool, value: &Option, returned: impl Fn(Option)->U +) -> Perhaps { + let mut value = value.unwrap_or(!*target); + if value == *target { + Ok(None) + } else { + std::mem::swap(target, &mut value); + Ok(Some(returned(Some(value)))) + } +} diff --git a/src/device/browse.rs b/src/device/browse.rs index e9bed1cf..82be27c1 100644 --- a/src/device/browse.rs +++ b/src/device/browse.rs @@ -19,8 +19,8 @@ impl Browse { todo!() } /// Set current directory - #[command(SetPath = "set-path")] - fn set_path (&mut self, _path: PathBuf) -> Perhaps { + #[command(Chdir = "chdir")] + fn chdir (&mut self, _path: PathBuf) -> Perhaps { todo!() } /// Set current filter @@ -35,15 +35,6 @@ impl Browse { } } -def_command!(FileBrowserCommand: |_browse: Browse|{ - //("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] Some(Self::Filter(f.expect("no filter"))))) -}); - /// Browses for files to load/save. /// /// ``` @@ -104,7 +95,7 @@ impl Browse { } Ok(Self { cwd, dirs, files, ..Default::default() }) } - pub fn chdir (&self) -> Usually { Self::new(Some(self.path())) } + pub fn to_chdir (&self) -> Usually { Self::new(Some(self.path())) } pub fn len (&self) -> usize { self.dirs.len() + self.files.len() } pub fn is_dir (&self) -> bool { self.index < self.dirs.len() } pub fn is_file (&self) -> bool { self.index >= self.dirs.len() } diff --git a/src/device/clock.rs b/src/device/clock.rs index e34ae47c..a59d179e 100644 --- a/src/device/clock.rs +++ b/src/device/clock.rs @@ -20,36 +20,61 @@ impl App { } } -def_command!(ClockCommand: |clock: Clock| { - SeekUsec { usec: f64 } => { - clock.playhead.update_from_usec(*usec); Ok(None) }, - SeekSample { sample: f64 } => { - clock.playhead.update_from_sample(*sample); Ok(None) }, - SeekPulse { pulse: f64 } => { - clock.playhead.update_from_pulse(*pulse); Ok(None) }, - SetBpm { bpm: f64 } => Ok(Some( - Self::SetBpm { bpm: clock.timebase().bpm.set(*bpm) })), - SetQuant { quant: f64 } => Ok(Some( - Self::SetQuant { quant: clock.quant.set(*quant) })), - SetSync { sync: f64 } => Ok(Some( - Self::SetSync { sync: clock.sync.set(*sync) })), +#[tek_proc::commands(ClockCommand = "clock")] +impl Clock { + #[command(SeekUsec = "usec")] + fn seek_usec (&mut self, usec: f64) -> Perhaps { + self.playhead.update_from_usec(usec); + Ok(None) + } - Play { position: Option } => { - clock.play_from(*position)?; Ok(None) /* TODO Some(Pause(previousPosition)) */ }, - Pause { position: Option } => { - clock.pause_at(*position)?; Ok(None) }, + #[command(SeekSample = "sample")] + fn seek_sample (&mut self, sample: f64) -> Perhaps { + self.playhead.update_from_sample(sample); + Ok(None) + } - TogglePlayback { position: u32 } => Ok(if clock.is_rolling() { - clock.pause_at(Some(*position))?; None - } else { - clock.play_from(Some(*position))?; None - }), -}); + #[command(SeekPulse = "pulse")] + fn seek_pulse (&mut self, pulse: f64) -> Perhaps { + self.playhead.update_from_pulse(pulse); + Ok(None) + } -impl +AsMut> HasClock for T {} -pub trait HasClock: AsRef + AsMut { - fn clock (&self) -> &Clock { self.as_ref() } - fn clock_mut (&mut self) -> &mut Clock { self.as_mut() } + #[command(SetBpm = "bpm")] + fn set_bpm (&mut self, bpm: f64) -> Perhaps { + Ok(Some(ClockCommand::SetBpm { bpm: self.timebase().bpm.set(bpm) })) + } + + #[command(SetQuant = "quant")] + fn set_quant (&mut self, quant: f64) -> Perhaps { + Ok(Some(ClockCommand::SetQuant { quant: self.quant.set(quant) })) + } + + #[command(SetSync = "sync")] + fn set_sync (&mut self, sync: f64) -> Perhaps { + Ok(Some(ClockCommand::SetSync { sync: self.sync.set(sync) })) + } + + #[command(Play = "play")] + fn play (&mut self, position: Option) -> Perhaps { + self.play_from(position)?; + Ok(None) /* TODO Some(Pause(previousPosition)) */ + } + + #[command(Pause = "pause")] + fn pause (&mut self, position: Option) -> Perhaps { + self.pause_at(position)?; + Ok(None) + } + + #[command(TogglePlayback = "toggle")] + fn toggle (&mut self, position: u32) -> Perhaps { + Ok(if self.is_rolling() { + self.pause_at(Some(position))?; None + } else { + self.play_from(Some(position))?; None + }) + } } /// The source of time. @@ -86,6 +111,23 @@ pub trait HasClock: AsRef + AsMut { #[cfg(feature = "port")] pub click_out: Arc>>, } +impl +AsMut> HasClock for T {} + +pub trait HasClock: AsRef + AsMut { + fn clock (&self) -> &Clock { + self.as_ref() + } + fn clock_mut (&mut self) -> &mut Clock { + self.as_mut() + } +} + +impl Act for ClockCommand { + fn act (&self, state: &mut T) -> Perhaps { + self.act(state.clock_mut()) // awesome + } +} + /// Quantization setting for launching clips /// /// ``` @@ -183,35 +225,6 @@ impl Quantize { } } -/// Define and implement a unit of time -#[macro_export] macro_rules! impl_time_unit { - ($T:ident) => { - impl Gettable for $T { - fn get (&self) -> f64 { self.0.load(Relaxed) } - } - impl InteriorMutable for $T { - fn set (&self, value: f64) -> f64 { - let old = self.get(); - self.0.store(value, Relaxed); - old - } - } - impl TimeUnit for $T {} - impl_op!($T, Add, add, |a, b|{a + b}); - impl_op!($T, Sub, sub, |a, b|{a - b}); - impl_op!($T, Mul, mul, |a, b|{a * b}); - impl_op!($T, Div, div, |a, b|{a / b}); - impl_op!($T, Rem, rem, |a, b|{a % b}); - impl From for $T { fn from (value: f64) -> Self { Self(value.into()) } } - impl From for $T { fn from (value: usize) -> Self { Self((value as f64).into()) } } - impl From<$T> for f64 { fn from (value: $T) -> Self { value.get() } } - impl From<$T> for usize { fn from (value: $T) -> Self { value.get() as usize } } - impl From<&$T> for f64 { fn from (value: &$T) -> Self { value.get() } } - impl From<&$T> for usize { fn from (value: &$T) -> Self { value.get() as usize } } - impl Clone for $T { fn clone (&self) -> Self { Self(self.get().into()) } } - } -} - impl std::fmt::Debug for Clock { fn fmt (&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { f.debug_struct("Clock") @@ -397,14 +410,38 @@ impl Clock { } } -impl Act for ClockCommand { - fn act (&self, state: &mut T) -> Perhaps { - self.act(state.clock_mut()) // awesome +impl_has!(Clock: |self: Track|self.sequencer.clock); +impl_default!(Timebase: Self::new(48000f64, 150f64, DEFAULT_PPQ)); + +/// Define and implement a unit of time +#[macro_export] macro_rules! impl_time_unit { + ($T:ident) => { + impl Gettable for $T { + fn get (&self) -> f64 { self.0.load(Relaxed) } + } + impl InteriorMutable for $T { + fn set (&self, value: f64) -> f64 { + let old = self.get(); + self.0.store(value, Relaxed); + old + } + } + impl TimeUnit for $T {} + impl_op!($T, Add, add, |a, b|{a + b}); + impl_op!($T, Sub, sub, |a, b|{a - b}); + impl_op!($T, Mul, mul, |a, b|{a * b}); + impl_op!($T, Div, div, |a, b|{a / b}); + impl_op!($T, Rem, rem, |a, b|{a % b}); + impl From for $T { fn from (value: f64) -> Self { Self(value.into()) } } + impl From for $T { fn from (value: usize) -> Self { Self((value as f64).into()) } } + impl From<$T> for f64 { fn from (value: $T) -> Self { value.get() } } + impl From<$T> for usize { fn from (value: $T) -> Self { value.get() as usize } } + impl From<&$T> for f64 { fn from (value: &$T) -> Self { value.get() } } + impl From<&$T> for usize { fn from (value: &$T) -> Self { value.get() as usize } } + impl Clone for $T { fn clone (&self) -> Self { Self(self.get().into()) } } } } -#[cfg(feature = "clock")] impl_has!(Clock: |self: Track|self.sequencer.clock); -impl_default!(Timebase: Self::new(48000f64, 150f64, DEFAULT_PPQ)); impl_time_unit!(SampleCount); impl_time_unit!(SampleRate); impl_time_unit!(Microsecond); diff --git a/src/device/editor.rs b/src/device/editor.rs index 44b50020..27354cfe 100644 --- a/src/device/editor.rs +++ b/src/device/editor.rs @@ -56,29 +56,69 @@ impl App { } } -def_command!(MidiEditCommand: |editor: MidiEditor| { - Show { clip: Option>> } => { - editor.set_clip(clip.as_ref()); editor.redraw(); Ok(None) }, - DeleteNote => { - editor.redraw(); todo!() }, - AppendNote { advance: bool } => { - editor.put_note(*advance); editor.redraw(); Ok(None) }, - SetNotePos { pos: usize } => { - editor.set_note_pos((*pos).min(127)); editor.redraw(); Ok(None) }, - SetNoteLen { len: usize } => { - editor.set_note_len(*len); editor.redraw(); Ok(None) }, - SetNoteScroll { scroll: usize } => { - editor.set_note_lo((*scroll).min(127)); editor.redraw(); Ok(None) }, - SetTimePos { pos: usize } => { - editor.set_time_pos(*pos); editor.redraw(); Ok(None) }, - SetTimeScroll { scroll: usize } => { - editor.set_time_start(*scroll); editor.redraw(); Ok(None) }, - SetTimeZoom { zoom: usize } => { - editor.set_time_zoom(*zoom); editor.redraw(); Ok(None) }, - SetTimeLock { lock: bool } => { - editor.set_time_lock(*lock); editor.redraw(); Ok(None) }, +#[tek_proc::commands(MidiEditCommand = "edit")] +impl MidiEditor { + #[command(Show = "show")] + fn show (&mut self, clip: Option>>) -> Perhaps { + self.set_clip(clip.as_ref()); + self.redraw(); + Ok(None) + } + #[command(DeleteNote = "delete")] + fn note_delete (&mut self) -> Perhaps { + self.redraw(); + todo!() + } + #[command(AppendNote = "append")] + fn note_append (&mut self, advance: bool) -> Perhaps { + self.put_note(advance); + self.redraw(); + Ok(None) + } + #[command(SetNotePos = "note-pos")] + fn note_set_pos (&mut self, pos: usize) -> Perhaps { + self.set_note_pos((pos).min(127)); + self.redraw(); + Ok(None) + } + #[command(SetNoteLen = "note-len")] + fn note_set_len (&mut self, len: usize) -> Perhaps { + self.set_note_len(len); + self.redraw(); + Ok(None) + } + #[command(SetNoteScroll = "note-scroll")] + fn note_set_scroll (&mut self, scroll: usize) -> Perhaps { + self.set_note_lo((scroll).min(127)); + self.redraw(); + Ok(None) + } + #[command(SetTimePos = "time-pos")] + fn time_set_pos (&mut self, pos: usize) -> Perhaps { + self.set_time_pos(pos); + self.redraw(); + Ok(None) + } + #[command(SetTimeScroll = "time-scroll")] + fn time_set_scroll (&mut self, scroll: usize) -> Perhaps { + self.set_time_start(scroll); + self.redraw(); + Ok(None) + } + #[command(SetTimeZoom = "time-zoom")] + fn time_set_zoom (&mut self, zoom: usize) -> Perhaps { + self.set_time_zoom(zoom); + self.redraw(); + Ok(None) + } + #[command(SetTimeLock = "time-lock")] + fn time_set_lock (&mut self, lock: bool) -> Perhaps { + self.set_time_lock(lock); + self.redraw(); + Ok(None) + } // TODO: 1-9 seek markers that by default start every 8th of the clip -}); +} /// Contains state for viewing and editing a clip. /// diff --git a/src/device/sampler.rs b/src/device/sampler.rs index 7da83c1d..206a8efa 100644 --- a/src/device/sampler.rs +++ b/src/device/sampler.rs @@ -8,52 +8,61 @@ pub(crate) use symphonia::{ }, }; -def_command!(SamplerCommand: |sampler: Sampler| { - RecordToggle { slot: usize } => { - let slot = *slot; - let recording = sampler.recording.as_ref().map(|x|x.0); - let _ = Self::RecordFinish.act(sampler)?; +#[tek_proc::commands(SamplerCommand = "sampler")] +impl Sampler { + + #[command(RecordToggle = "rec-toggle")] + fn record_toggle (&mut self, slot: usize) -> Perhaps { + let recording = self.recording.as_ref().map(|x|x.0); + let _ = SamplerCommand::RecordFinish.act(self)?; // autoslice: continue recording at next slot if recording != Some(slot) { - Self::RecordBegin { slot }.act(sampler) + SamplerCommand::RecordBegin { slot }.act(self) } else { Ok(None) } - }, - RecordBegin { slot: usize } => { - let slot = *slot; - sampler.recording = Some(( + } + + #[command(RecordBegin = "rec-begin")] + fn record_begin (&mut self, slot: usize) -> Perhaps { + self.recording = Some(( slot, Some(Arc::new(RwLock::new(Sample::new( - "Sample", 0, 0, vec![vec![];sampler.audio_ins.len()] + "Sample", 0, 0, vec![vec![];self.audio_ins.len()] )))) )); Ok(None) - }, - RecordFinish => { - let _prev_sample = sampler.recording.as_mut().map(|(index, sample)|{ - std::mem::swap(sample, &mut sampler.samples.0[*index]); + } + + #[command(RecordFinish = "rec-finish")] + fn record_finish (&mut self) -> Perhaps { + let _prev_sample = self.recording.as_mut().map(|(index, sample)|{ + std::mem::swap(sample, &mut self.samples.0[*index]); sample }); // TODO: undo Ok(None) - }, - RecordCancel => { - sampler.recording = None; + } + + #[command(RecordCancel = "rec-cancel")] + fn record_cancel (&mut self) -> Perhaps { + self.recording = None; Ok(None) - }, - PlaySample { slot: usize } => { - let slot = *slot; - if let Some(ref sample) = sampler.samples.0[slot] { - sampler.voices.write().unwrap().push(Sample::play(sample, 0, &u7::from(128))); + } + + #[command(PlaySample = "sample-play")] + fn sample_play (&mut self, slot: usize) -> Perhaps { + if let Some(ref sample) = self.samples.0[slot] { + self.voices.write().unwrap().push(Sample::play(sample, 0, &u7::from(128))); } Ok(None) - }, - StopSample { slot: usize } => { - let _slot = *slot; + } + + #[command(StopSample = "sample-stop")] + fn sample_stop (&mut self, slot: usize) -> Perhaps { todo!(); //Ok(None) - }, -}); + } +} /// Plays [Voice]s from [Sample]s. /// diff --git a/src/tek.rs b/src/tek.rs index f0f7fedb..5bac9752 100644 --- a/src/tek.rs +++ b/src/tek.rs @@ -877,58 +877,14 @@ pub use self::bind::*; mod bind { use crate::*; - /// Define an enum containing commands, and implement [Command] trait for over given `State`. - #[macro_export] macro_rules! def_command ( - ($Command:ident: |$state:ident: $State:ty| { - // FIXME: support attrs (docstrings) - $($Variant:ident$({$($arg:ident:$Arg:ty),+ $(,)?})?=>$body:expr),* $(,)? - })=>{ - #[derive(Debug)] pub enum $Command { - // FIXME: support attrs (docstrings) - $($Variant $({ $($arg: $Arg),* })?),* - } - impl ::tengri::dizzle::Act<$State> for $Command { - fn act (&self, $state: &mut $State) -> Perhaps { - match self { - $(Self::$Variant $({ $($arg),* })? => $body,)* - _ => unimplemented!("Act<{}>: {self:?}", stringify!($State)), - } - } - } - }); - - pub fn swap_value ( - target: &mut T, value: &T, returned: impl Fn(T)->U - ) -> Perhaps { - if *target == *value { - Ok(None) - } else { - let mut value = value.clone(); - std::mem::swap(target, &mut value); - Ok(Some(returned(value))) - } - } - - pub fn toggle_bool ( - target: &mut bool, value: &Option, returned: impl Fn(Option)->U - ) -> Perhaps { - let mut value = value.unwrap_or(!*target); - if value == *target { - Ok(None) - } else { - std::mem::swap(target, &mut value); - Ok(Some(returned(Some(value)))) - } - } - tui_keys!(self: App, input { - let commands = tek_commands_collect(self, input)?; - let results = tek_commands_execute(self, commands)?; + let commands = collect_commands(self, input)?; + let results = execute_commands(self, commands)?; self.history.extend(results.into_iter()); Ok(()) }); - fn tek_commands_collect (app: &App, input: &TuiEvent) + fn collect_commands (app: &App, input: &TuiEvent) -> Usually> { let mut commands = vec![]; @@ -953,7 +909,7 @@ mod bind { Ok(commands) } - fn tek_commands_execute (app: &mut App, commands: Vec) + fn execute_commands (app: &mut App, commands: Vec) -> Usually)>> { let mut history = vec![];