diff --git a/crates/app/src/api.rs b/crates/app/src/api.rs index 93475cc1..95486435 100644 --- a/crates/app/src/api.rs +++ b/crates/app/src/api.rs @@ -85,6 +85,360 @@ expose!([self: Tek] { } }); +macro_rules! defcom { + (|$self:ident, $app:ident:$App:ty| $($Command:ident { $( + $Variant:ident $(($($param:ident: $Param:ty),+))? => $expr:expr + )* $(,)? })*) => { + $(#[derive(Clone, Debug)] pub enum $Command { + $($Variant $(($($Param),+))?),* + })* + $(command!(|$self: $Command, $app: $App|match $self { + $($Command::$Variant $(($($param),+))? => $expr),* + });)* + } +} + +defcom! { |self, app: Tek| + + TekCommand { + + Sampler(cmd: SamplerCommand) => { + println!("\n\rtodo: {cmd:?}"); + None + } + + Enqueue(clip: Option>>) => { + println!("\n\rtodo: enqueue {clip:?}"); + None + } + + History(delta: isize) => { + println!("\n\rtodo: {self:?}"); + None + } + + Zoom(zoom: Option) => { + println!("\n\rtodo: {self:?}"); + None + } + + Scene(cmd: SceneCommand) => + cmd.delegate(app, Self::Scene)? + + Track(cmd: TrackCommand) => + cmd.delegate(app, Self::Track)? + + Output(cmd: OutputCommand) => + cmd.delegate(app, Self::Output)? + + Input(cmd: InputCommand) => + cmd.delegate(app, Self::Input)? + + Clip(cmd: ClipCommand) => + cmd.delegate(app, Self::Clip)? + + Clock(cmd: ClockCommand) => + cmd.delegate(app, Self::Clock)? + + Editor(cmd: MidiEditCommand) => app.editor.as_mut() + .map(|editor|cmd.delegate(editor, Self::Editor)) + .transpose()? + .flatten() + + Pool(cmd: PoolCommand) => if let Some(pool) = app.pool.as_mut() { + let undo = cmd.clone().delegate(pool, Self::Pool)?; + if let Some(editor) = app.editor.as_mut() { + match cmd { + // autoselect: automatically load selected clip in editor + // autocolor: update color in all places simultaneously + PoolCommand::Select(_) | PoolCommand::Clip(PoolClipCommand::SetColor(_, _)) => + editor.set_clip(pool.clip().as_ref()), + _ => {} + } + }; + undo + } else { + None + } + + Color(palette: ItemPalette) => { + use Selection::*; + Some(Self::Color(match app.selected { + Mix => { + let old = app.color; + app.color = palette; + old + }, + Track(t) => { + let old = app.tracks[t].color; + app.tracks[t].color = palette; + old + } + Scene(s) => { + let old = app.scenes[s].color; + app.scenes[s].color = palette; + old + } + Clip(t, s) => { + if let Some(ref clip) = app.scenes[s].clips[t] { + let mut clip = clip.write().unwrap(); + let old = clip.color; + clip.color = palette; + old + } else { + return Ok(None) + } + } + })) + } + + Edit(value: Option) => { + if let Some(value) = value { + if app.is_editing() != value { + app.editing.store(value, Relaxed); + } + } else { + app.editing.store(!app.is_editing(), Relaxed); + }; + // autocreate: create new clip from pool when entering empty cell + if let Some(ref pool) = app.pool { + if app.is_editing() { + if let Selection::Clip(t, s) = app.selected { + if let Some(scene) = app.scenes.get_mut(s) { + if let Some(slot) = scene.clips.get_mut(t) { + if slot.is_none() { + let (index, mut clip) = pool.add_new_clip(); + // autocolor: new clip colors from scene and track color + clip.write().unwrap().color = ItemColor::random_near( + app.tracks[t].color.base.mix( + scene.color.base, + 0.5 + ), + 0.2 + ).into(); + if let Some(ref mut editor) = app.editor { + editor.set_clip(Some(&clip)); + } + *slot = Some(clip); + } + } + } + } + } + } + None + } + + Launch => { + use Selection::*; + match app.selected { + Track(t) => app.tracks[t].player.enqueue_next(None), + Clip(t, s) => app.tracks[t].player.enqueue_next(app.scenes[s].clips[t].as_ref()), + Scene(s) => { + for t in 0..app.tracks.len() { + app.tracks[t].player.enqueue_next(app.scenes[s].clips[t].as_ref()) + } + }, + _ => {} + }; + None + } + + Select(s: Selection) => { + app.selected = s; + // autoedit: load focused clip in editor. + if let Some(ref mut editor) = app.editor { + editor.set_clip(match app.selected { + Selection::Clip(t, s) if let Some(Some(Some(clip))) = app + .scenes.get(s).map(|s|s.clips.get(t)) => Some(clip), + _ => None + }); + } + None + } + + StopAll => { + for track in 0..app.tracks.len(){app.tracks[track].player.enqueue_next(None);} + None + } + + } + + InputCommand { + + Add => { + app.midi_ins.push(JackMidiIn::new(&app.jack, &format!("M/{}", app.midi_ins.len()), &[])?); + None + } + + } + + OutputCommand { + + Add => { + app.midi_outs.push(JackMidiOut::new(&app.jack, &format!("{}/M", app.midi_outs.len()), &[])?); + None + } + + } + + TrackCommand { + + Swap(a: usize, b: usize) => { + println!("\n\rtodo: {self:?}"); + None + } + + SetSize(t: usize) => { + println!("\n\rtodo: {self:?}"); + None + } + + SetZoom(z: usize) => { + println!("\n\rtodo: {self:?}"); + None + } + + Add => { + use Selection::*; + let index = app.track_add(None, None, &[], &[])?.0; + app.selected = match app.selected { + Track(t) => Track(index), + Clip(t, s) => Clip(index, s), + _ => app.selected + }; + Some(Self::Del(index)) + } + + Del(index: usize) => { + app.track_del(index); + None + } + + Stop(index: usize) => { + app.tracks[index].player.enqueue_next(None); + None + } + + SetColor(index: usize, color: ItemPalette) => { + let old = app.tracks[index].color; + app.tracks[index].color = color; + Some(Self::SetColor(index, old)) + } + + TogglePlay => { + Some(Self::TogglePlay) + } + + ToggleSolo => { + Some(Self::ToggleSolo) + } + + ToggleRecord => { + if let Some(t) = app.selected.track() { + app.tracks[t-1].player.recording = !app.tracks[t-1].player.recording; + } + Some(Self::ToggleRecord) + } + + ToggleMonitor => { + if let Some(t) = app.selected.track() { + app.tracks[t-1].player.monitoring = !app.tracks[t-1].player.monitoring; + } + Some(Self::ToggleMonitor) + } + + } + + SceneCommand { + + Swap(a: usize, b: usize) => { + println!("\n\rtodo: {self:?}"); + None + } + + SetSize(index: usize) => { + println!("\n\rtodo: {self:?}"); + None + } + + SetZoom(zoom: usize) => { + println!("\n\rtodo: {self:?}"); + None + } + + Add => { + use Selection::*; + let index = app.scene_add(None, None)?.0; + app.selected = match app.selected { + Scene(s) => Scene(index), + Clip(t, s) => Clip(t, index), + _ => app.selected + }; + Some(Self::Del(index)) + } + + Del(index: usize) => { + app.scene_del(index); + None + } + + SetColor(index: usize, color: ItemPalette) => { + let old = app.scenes[index].color; + app.scenes[index].color = color; + Some(Self::SetColor(index, old)) + } + + Enqueue(scene: usize) => { + for track in 0..app.tracks.len() { + app.tracks[track].player.enqueue_next(app.scenes[scene].clips[track].as_ref()); + } + None + } + + } + + ClipCommand { + + Get(a: usize, b: usize) => { + println!("\n\rtodo: {self:?}"); + None + } + + Edit(clip: Option>>) => { + println!("\n\rtodo: edit {clip:?}"); + None + } + + SetLoop(track: usize, scene: usize, looped: bool) => { + println!("\n\rtodo: {self:?}"); + None + } + + Put(track: usize, scene: usize, clip: Option>>) => { + let old = app.scenes[scene].clips[track].clone(); + app.scenes[scene].clips[track] = clip; + Some(Self::Put(track, scene, old)) + } + + Enqueue(track: usize, scene: usize) => { + app.tracks[track].player.enqueue_next(app.scenes[scene].clips[track].as_ref()); + None + } + + SetColor(track: usize, scene: usize, color: ItemPalette) => { + app.scenes[scene].clips[track].as_ref().map(|clip|{ + let mut clip = clip.write().unwrap(); + let old = clip.color.clone(); + clip.color = color.clone(); + panic!("{color:?} {old:?}"); + Self::SetColor(track, scene, old) + }) + } + + } +} + impose!([app: Tek] { TekCommand => { @@ -201,3 +555,28 @@ impose!([app: Tek] { } }); + +handle!(TuiIn: |self: Tek, input|Ok({ + // If editing, editor keys take priority + if self.is_editing() { + if self.editor.handle(input)? == Some(true) { + return Ok(Some(true)) + } + } + // Handle from root keymap + if let Some(command) = self.keys.command::<_, TekCommand, _>(self, input) { + if let Some(undo) = command.execute(self)? { self.history.push(undo); } + return Ok(Some(true)) + } + // Handle from selection-dependent keymaps + if let Some(command) = match self.selected() { + Selection::Clip(_, _) => self.keys_clip, + Selection::Track(_) => self.keys_track, + Selection::Scene(_) => self.keys_scene, + Selection::Mix => self.keys_mix, + }.command::<_, TekCommand, _>(self, input) { + if let Some(undo) = command.execute(self)? { self.history.push(undo); } + return Ok(Some(true)) + } + None +})); diff --git a/crates/app/src/keys.rs b/crates/app/src/keys.rs deleted file mode 100644 index 30f4daf0..00000000 --- a/crates/app/src/keys.rs +++ /dev/null @@ -1,355 +0,0 @@ -use crate::*; - -handle!(TuiIn: |self: Tek, input|Ok({ - // If editing, editor keys take priority - if self.is_editing() { - if self.editor.handle(input)? == Some(true) { - return Ok(Some(true)) - } - } - // Handle from root keymap - if let Some(command) = self.keys.command::<_, TekCommand, _>(self, input) { - if let Some(undo) = command.execute(self)? { self.history.push(undo); } - return Ok(Some(true)) - } - // Handle from selection-dependent keymaps - if let Some(command) = match self.selected() { - Selection::Clip(_, _) => self.keys_clip, - Selection::Track(_) => self.keys_track, - Selection::Scene(_) => self.keys_scene, - Selection::Mix => self.keys_mix, - }.command::<_, TekCommand, _>(self, input) { - if let Some(undo) = command.execute(self)? { self.history.push(undo); } - return Ok(Some(true)) - } - None -})); - -macro_rules! defcom { - (|$self:ident, $app:ident:$App:ty| $($Command:ident { $( - $Variant:ident $(($($param:ident: $Param:ty),+))? => $expr:expr - )* $(,)? })*) => { - $(#[derive(Clone, Debug)] pub enum $Command { - $($Variant $(($($Param),+))?),* - })* - $(command!(|$self: $Command, $app: $App|match $self { - $($Command::$Variant $(($($param),+))? => $expr),* - });)* - } -} - -defcom! { |self, app: Tek| - - TekCommand { - - Sampler(cmd: SamplerCommand) => { - println!("\n\rtodo: {cmd:?}"); - None - } - - Enqueue(clip: Option>>) => { - println!("\n\rtodo: enqueue {clip:?}"); - None - } - - History(delta: isize) => { - println!("\n\rtodo: {self:?}"); - None - } - - Zoom(zoom: Option) => { - println!("\n\rtodo: {self:?}"); - None - } - - Scene(cmd: SceneCommand) => - cmd.delegate(app, Self::Scene)? - - Track(cmd: TrackCommand) => - cmd.delegate(app, Self::Track)? - - Output(cmd: OutputCommand) => - cmd.delegate(app, Self::Output)? - - Input(cmd: InputCommand) => - cmd.delegate(app, Self::Input)? - - Clip(cmd: ClipCommand) => - cmd.delegate(app, Self::Clip)? - - Clock(cmd: ClockCommand) => - cmd.delegate(app, Self::Clock)? - - Editor(cmd: MidiEditCommand) => app.editor.as_mut() - .map(|editor|cmd.delegate(editor, Self::Editor)) - .transpose()? - .flatten() - - Pool(cmd: PoolCommand) => if let Some(pool) = app.pool.as_mut() { - let undo = cmd.clone().delegate(pool, Self::Pool)?; - if let Some(editor) = app.editor.as_mut() { - match cmd { - // autoselect: automatically load selected clip in editor - // autocolor: update color in all places simultaneously - PoolCommand::Select(_) | PoolCommand::Clip(PoolClipCommand::SetColor(_, _)) => - editor.set_clip(pool.clip().as_ref()), - _ => {} - } - }; - undo - } else { - None - } - - Color(palette: ItemPalette) => { - use Selection::*; - Some(Self::Color(match app.selected { - Mix => { - let old = app.color; - app.color = palette; - old - }, - Track(t) => { - let old = app.tracks[t].color; - app.tracks[t].color = palette; - old - } - Scene(s) => { - let old = app.scenes[s].color; - app.scenes[s].color = palette; - old - } - Clip(t, s) => { - if let Some(ref clip) = app.scenes[s].clips[t] { - let mut clip = clip.write().unwrap(); - let old = clip.color; - clip.color = palette; - old - } else { - return Ok(None) - } - } - })) - } - - Edit(value: Option) => { - if let Some(value) = value { - if app.is_editing() != value { - app.editing.store(value, Relaxed); - } - } else { - app.editing.store(!app.is_editing(), Relaxed); - }; - // autocreate: create new clip from pool when entering empty cell - if let Some(ref pool) = app.pool { - if app.is_editing() { - if let Selection::Clip(t, s) = app.selected { - if let Some(scene) = app.scenes.get_mut(s) { - if let Some(slot) = scene.clips.get_mut(t) { - if slot.is_none() { - let (index, mut clip) = pool.add_new_clip(); - // autocolor: new clip colors from scene and track color - clip.write().unwrap().color = ItemColor::random_near( - app.tracks[t].color.base.mix( - scene.color.base, - 0.5 - ), - 0.2 - ).into(); - if let Some(ref mut editor) = app.editor { - editor.set_clip(Some(&clip)); - } - *slot = Some(clip); - } - } - } - } - } - } - None - } - - Launch => { - use Selection::*; - match app.selected { - Track(t) => app.tracks[t].player.enqueue_next(None), - Clip(t, s) => app.tracks[t].player.enqueue_next(app.scenes[s].clips[t].as_ref()), - Scene(s) => { - for t in 0..app.tracks.len() { - app.tracks[t].player.enqueue_next(app.scenes[s].clips[t].as_ref()) - } - }, - _ => {} - }; - None - } - - Select(s: Selection) => { - app.selected = s; - // autoedit: load focused clip in editor. - if let Some(ref mut editor) = app.editor { - editor.set_clip(match app.selected { - Selection::Clip(t, s) if let Some(Some(Some(clip))) = app - .scenes.get(s).map(|s|s.clips.get(t)) => Some(clip), - _ => None - }); - } - None - } - - StopAll => { - for track in 0..app.tracks.len(){app.tracks[track].player.enqueue_next(None);} - None - } - - } - - InputCommand { - - Add => { - app.midi_ins.push(JackMidiIn::new(&app.jack, &format!("M/{}", app.midi_ins.len()), &[])?); - None - } - - } - - OutputCommand { - - Add => { - app.midi_outs.push(JackMidiOut::new(&app.jack, &format!("{}/M", app.midi_outs.len()), &[])?); - None - } - - } - - TrackCommand { - - Swap(a: usize, b: usize) => { - println!("\n\rtodo: {self:?}"); - None - } - - SetSize(t: usize) => { - println!("\n\rtodo: {self:?}"); - None - } - - SetZoom(z: usize) => { - println!("\n\rtodo: {self:?}"); - None - } - - Add => { - use Selection::*; - let index = app.track_add(None, None, &[], &[])?.0; - app.selected = match app.selected { - Track(t) => Track(index), - Clip(t, s) => Clip(index, s), - _ => app.selected - }; - Some(Self::Del(index)) - } - Del(index: usize) => { - app.track_del(index); - None - } - Stop(index: usize) => { - app.tracks[index].player.enqueue_next(None); - None - } - SetColor(index: usize, color: ItemPalette) => { - let old = app.tracks[index].color; - app.tracks[index].color = color; - Some(Self::SetColor(index, old)) - } - TogglePlay => { - Some(Self::TogglePlay) - } - ToggleSolo => { - Some(Self::ToggleSolo) - } - ToggleRecord => { - if let Some(t) = app.selected.track() { - app.tracks[t-1].player.recording = !app.tracks[t-1].player.recording; - } - Some(Self::ToggleRecord) - } - ToggleMonitor => { - if let Some(t) = app.selected.track() { - app.tracks[t-1].player.monitoring = !app.tracks[t-1].player.monitoring; - } - Some(Self::ToggleMonitor) - } - } - SceneCommand { - Swap(a: usize, b: usize) => { - println!("\n\rtodo: {self:?}"); - None - } - SetSize(index: usize) => { - println!("\n\rtodo: {self:?}"); - None - } - SetZoom(zoom: usize) => { - println!("\n\rtodo: {self:?}"); - None - } - Add => { - use Selection::*; - let index = app.scene_add(None, None)?.0; - app.selected = match app.selected { - Scene(s) => Scene(index), - Clip(t, s) => Clip(t, index), - _ => app.selected - }; - Some(Self::Del(index)) - } - Del(index: usize) => { - app.scene_del(index); - None - } - SetColor(index: usize, color: ItemPalette) => { - let old = app.scenes[index].color; - app.scenes[index].color = color; - Some(Self::SetColor(index, old)) - } - Enqueue(scene: usize) => { - for track in 0..app.tracks.len() { - app.tracks[track].player.enqueue_next(app.scenes[scene].clips[track].as_ref()); - } - None - } - } - ClipCommand { - Get(a: usize, b: usize) => { - println!("\n\rtodo: {self:?}"); - None - } - Edit(clip: Option>>) => { - println!("\n\rtodo: edit {clip:?}"); - None - } - SetLoop(track: usize, scene: usize, looped: bool) => { - println!("\n\rtodo: {self:?}"); - None - } - Put(track: usize, scene: usize, clip: Option>>) => { - let old = app.scenes[scene].clips[track].clone(); - app.scenes[scene].clips[track] = clip; - Some(Self::Put(track, scene, old)) - } - Enqueue(track: usize, scene: usize) => { - app.tracks[track].player.enqueue_next(app.scenes[scene].clips[track].as_ref()); - None - } - SetColor(track: usize, scene: usize, color: ItemPalette) => { - app.scenes[scene].clips[track].as_ref().map(|clip|{ - let mut clip = clip.write().unwrap(); - let old = clip.color.clone(); - clip.color = color.clone(); - panic!("{color:?} {old:?}"); - Self::SetColor(track, scene, old) - }) - } - } -} diff --git a/crates/app/src/lib.rs b/crates/app/src/lib.rs index e7d7e790..16ecd001 100644 --- a/crates/app/src/lib.rs +++ b/crates/app/src/lib.rs @@ -38,7 +38,6 @@ pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::Relaxed}} mod api; pub use self::api::*; mod audio; pub use self::audio::*; mod device; pub use self::device::*; -mod keys; pub use self::keys::*; mod model; pub use self::model::*; mod view; pub use self::view::*;