From 8dcf73c18ca604667b31d1e17c9634aafc9aee8d Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sun, 12 Jan 2025 01:16:05 +0100 Subject: [PATCH] nice top level command dispatch --- midi/src/midi_editor.rs | 10 ++-- midi/src/midi_pool.rs | 30 +++++------ sampler/src/lib.rs | 1 + sampler/src/sampler.rs | 88 ------------------------------ sampler/src/sampler_cmd.rs | 89 +++++++++++++++++++++++++++++++ tek/examples/midi_import.rs.fixme | 2 +- tek/src/control.rs | 56 +++++++++---------- time/src/clock.rs | 5 +- 8 files changed, 137 insertions(+), 144 deletions(-) create mode 100644 sampler/src/sampler_cmd.rs diff --git a/midi/src/midi_editor.rs b/midi/src/midi_editor.rs index e511820c..3e93d694 100644 --- a/midi/src/midi_editor.rs +++ b/midi/src/midi_editor.rs @@ -152,11 +152,6 @@ pub enum MidiEditCommand { SetTimeLock(bool), Show(Option>>), } -impl MidiEditCommand { - pub fn from_edn <'a> (head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { - todo!() - } -} handle!(TuiIn: |self: MidiEditor, input|MidiEditCommand::execute_with_state(self, input.event())); keymap!(KEYS_MIDI_EDITOR = |s: MidiEditor, _input: Event| MidiEditCommand { key(Up) => SetNoteCursor(s.note_point() + 1), @@ -217,3 +212,8 @@ impl Command for MidiEditCommand { Ok(None) } } +impl EdnCommand for MidiEditCommand { + fn from_edn <'a> (state: &MidiEditor, head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { + todo!() + } +} diff --git a/midi/src/midi_pool.rs b/midi/src/midi_pool.rs index 6ed53f2f..1eba2460 100644 --- a/midi/src/midi_pool.rs +++ b/midi/src/midi_pool.rs @@ -56,7 +56,7 @@ from!(|clip:&Arc>|PoolModel = { pub enum PoolCommand { Show(bool), /// Update the contents of the clip pool - Clip(MidiPoolCommand), + Clip(PoolClipCommand), /// Select a clip from the clip pool Select(usize), /// Rename a clip @@ -68,13 +68,13 @@ pub enum PoolCommand { /// Export to file Export(FileBrowserCommand), } -impl PoolCommand { - pub fn from_edn <'a> (head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { +impl EdnCommand for PoolCommand { + fn from_edn <'a> (state: &PoolModel, head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { todo!() } } #[derive(Clone, Debug, PartialEq)] -pub enum MidiPoolCommand { +pub enum PoolClipCommand { Add(usize, MidiClip), Delete(usize), Swap(usize, usize), @@ -84,7 +84,7 @@ pub enum MidiPoolCommand { SetLength(usize, usize), SetColor(usize, ItemColor), } -impl MidiPoolCommand { +impl PoolClipCommand { pub fn from_edn <'a> (head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { todo!() } @@ -104,9 +104,9 @@ pub enum PoolMode { } -impl Command for MidiPoolCommand { +impl Command for PoolClipCommand { fn execute (self, model: &mut T) -> Perhaps { - use MidiPoolCommand::*; + use PoolClipCommand::*; Ok(match self { Add(mut index, clip) => { let clip = Arc::new(RwLock::new(clip)); @@ -268,7 +268,7 @@ fn to_clips_command (state: &PoolModel, input: &Event) -> Option { kpat!(Char('t')) => Cmd::Length(ClipLengthCommand::Begin), kpat!(Char('m')) => Cmd::Import(FileBrowserCommand::Begin), kpat!(Char('x')) => Cmd::Export(FileBrowserCommand::Begin), - kpat!(Char('c')) => Cmd::Clip(MidiPoolCommand::SetColor(index, ItemColor::random())), + kpat!(Char('c')) => Cmd::Clip(PoolClipCommand::SetColor(index, ItemColor::random())), kpat!(Char('[')) | kpat!(Up) => Cmd::Select( index.overflowing_sub(1).0.min(state.clips().len() - 1) ), @@ -277,32 +277,32 @@ fn to_clips_command (state: &PoolModel, input: &Event) -> Option { ), kpat!(Char('<')) => if index > 1 { state.set_clip_index(state.clip_index().saturating_sub(1)); - Cmd::Clip(MidiPoolCommand::Swap(index - 1, index)) + Cmd::Clip(PoolClipCommand::Swap(index - 1, index)) } else { return None }, kpat!(Char('>')) => if index < count.saturating_sub(1) { state.set_clip_index(state.clip_index() + 1); - Cmd::Clip(MidiPoolCommand::Swap(index + 1, index)) + Cmd::Clip(PoolClipCommand::Swap(index + 1, index)) } else { return None }, kpat!(Delete) => if index > 0 { state.set_clip_index(index.min(count.saturating_sub(1))); - Cmd::Clip(MidiPoolCommand::Delete(index)) + Cmd::Clip(PoolClipCommand::Delete(index)) } else { return None }, - kpat!(Char('a')) | kpat!(Shift-Char('A')) => Cmd::Clip(MidiPoolCommand::Add(count, MidiClip::new( + kpat!(Char('a')) | kpat!(Shift-Char('A')) => Cmd::Clip(PoolClipCommand::Add(count, MidiClip::new( "Clip", true, 4 * PPQ, None, Some(ItemPalette::random()) ))), - kpat!(Char('i')) => Cmd::Clip(MidiPoolCommand::Add(index + 1, MidiClip::new( + kpat!(Char('i')) => Cmd::Clip(PoolClipCommand::Add(index + 1, MidiClip::new( "Clip", true, 4 * PPQ, None, Some(ItemPalette::random()) ))), kpat!(Char('d')) | kpat!(Shift-Char('D')) => { let mut clip = state.clips()[index].read().unwrap().duplicate(); clip.color = ItemPalette::random_near(clip.color, 0.25); - Cmd::Clip(MidiPoolCommand::Add(index + 1, clip)) + Cmd::Clip(PoolClipCommand::Add(index + 1, clip)) }, _ => return None }) @@ -343,7 +343,7 @@ command!(|self: FileBrowserCommand, state: PoolModel|{ let index = *index; let path = browser.path(); *mode = None; - MidiPoolCommand::Import(index, path).execute(state)?; + PoolClipCommand::Import(index, path).execute(state)?; } else if browser.is_dir() { *mode = Some(Import(*index, browser.chdir()?)); }, diff --git a/sampler/src/lib.rs b/sampler/src/lib.rs index ba29ef8f..df1d1f3e 100644 --- a/sampler/src/lib.rs +++ b/sampler/src/lib.rs @@ -1,5 +1,6 @@ mod sampler; pub use self::sampler::*; mod sampler_tui; pub use self::sampler_tui::*; +mod sampler_cmd; pub use self::sampler_cmd::*; pub(crate) use ::tek_jack::{*, jack::*}; pub(crate) use ::tek_midi::{*, midly::{*, live::*, num::*}}; diff --git a/sampler/src/sampler.rs b/sampler/src/sampler.rs index b1a4c439..f5c4cfcc 100644 --- a/sampler/src/sampler.rs +++ b/sampler/src/sampler.rs @@ -415,14 +415,6 @@ impl Iterator for Voice { } } -input_to_command!(FileBrowserCommand: |state:SamplerTui, input: Event|match input { - _ => return None -}); - -command!(|self:FileBrowserCommand,state:SamplerTui|match self { - _ => todo!() -}); - pub struct AddSampleModal { exited: bool, dir: PathBuf, @@ -656,86 +648,6 @@ impl Content for AddSampleModal { //}); -handle!(TuiIn: |self: SamplerTui, input|SamplerTuiCommand::execute_with_state(self, input.event())); - -#[derive(Clone, Debug)] pub enum SamplerTuiCommand { - Import(FileBrowserCommand), - Select(usize), - Sample(SamplerCommand), -} -impl SamplerTuiCommand { - pub fn from_edn <'a> (head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { - todo!() - } -} - -#[derive(Clone, Debug)] pub enum SamplerCommand { - RecordBegin(u7), - RecordCancel, - RecordFinish, - SetSample(u7, Option>>), - SetStart(u7, usize), - SetGain(f32), - NoteOn(u7, u7), - NoteOff(u7), -} -impl SamplerCommand { - pub fn from_edn <'a> (head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { - todo!() - } -} - -input_to_command!(SamplerTuiCommand: |state: SamplerTui, input: Event|match state.mode{ - Some(SamplerMode::Import(..)) => Self::Import( - FileBrowserCommand::input_to_command(state, input)? - ), - _ => match input { - // load sample - kpat!(Shift-Char('L')) => Self::Import(FileBrowserCommand::Begin), - kpat!(KeyCode::Up) => Self::Select(state.note_point().overflowing_add(1).0.min(127)), - kpat!(KeyCode::Down) => Self::Select(state.note_point().overflowing_sub(1).0.min(127)), - _ => return None - } -}); - -command!(|self: SamplerTuiCommand, state: SamplerTui|match self { - Self::Import(FileBrowserCommand::Begin) => { - let voices = &state.state.voices; - let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![]))); - state.mode = Some(SamplerMode::Import(0, FileBrowser::new(None)?)); - None - }, - Self::Select(index) => { - let old = state.note_point(); - state.set_note_point(index); - Some(Self::Select(old)) - }, - Self::Sample(cmd) => cmd.execute(&mut state.state)?.map(Self::Sample), - _ => todo!() -}); - -command!(|self: SamplerCommand, state: Sampler|match self { - Self::SetSample(index, sample) => { - let i = index.as_int() as usize; - let old = state.mapped[i].clone(); - state.mapped[i] = sample; - Some(Self::SetSample(index, old)) - }, - Self::RecordBegin(index) => { - state.begin_recording(index.as_int() as usize); - None - }, - Self::RecordCancel => { - state.cancel_recording(); - None - }, - Self::RecordFinish => { - state.finish_recording(); - None - }, - _ => todo!() -}); - pub enum SamplerMode { // Load sample from path Import(usize, FileBrowser), diff --git a/sampler/src/sampler_cmd.rs b/sampler/src/sampler_cmd.rs new file mode 100644 index 00000000..fe4bf042 --- /dev/null +++ b/sampler/src/sampler_cmd.rs @@ -0,0 +1,89 @@ +use crate::*; + +handle!(TuiIn: |self: SamplerTui, input|SamplerTuiCommand::execute_with_state(self, input.event())); + +#[derive(Clone, Debug)] pub enum SamplerTuiCommand { + Import(FileBrowserCommand), + Select(usize), + Sample(SamplerCommand), +} +impl EdnCommand for SamplerTuiCommand { + fn from_edn <'a> (state: &SamplerTui, head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { + todo!() + } +} + +#[derive(Clone, Debug)] pub enum SamplerCommand { + RecordBegin(u7), + RecordCancel, + RecordFinish, + SetSample(u7, Option>>), + SetStart(u7, usize), + SetGain(f32), + NoteOn(u7, u7), + NoteOff(u7), +} +impl EdnCommand for SamplerCommand { + fn from_edn <'a> (state: &Sampler, head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { + todo!() + } +} + +input_to_command!(FileBrowserCommand: |state:SamplerTui, input: Event|match input { + _ => return None +}); + +command!(|self:FileBrowserCommand,state:SamplerTui|match self { + _ => todo!() +}); + +input_to_command!(SamplerTuiCommand: |state: SamplerTui, input: Event|match state.mode{ + Some(SamplerMode::Import(..)) => Self::Import( + FileBrowserCommand::input_to_command(state, input)? + ), + _ => match input { + // load sample + kpat!(Shift-Char('L')) => Self::Import(FileBrowserCommand::Begin), + kpat!(KeyCode::Up) => Self::Select(state.note_point().overflowing_add(1).0.min(127)), + kpat!(KeyCode::Down) => Self::Select(state.note_point().overflowing_sub(1).0.min(127)), + _ => return None + } +}); + +command!(|self: SamplerTuiCommand, state: SamplerTui|match self { + Self::Import(FileBrowserCommand::Begin) => { + let voices = &state.state.voices; + let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![]))); + state.mode = Some(SamplerMode::Import(0, FileBrowser::new(None)?)); + None + }, + Self::Select(index) => { + let old = state.note_point(); + state.set_note_point(index); + Some(Self::Select(old)) + }, + Self::Sample(cmd) => cmd.execute(&mut state.state)?.map(Self::Sample), + _ => todo!() +}); + +command!(|self: SamplerCommand, state: Sampler|match self { + Self::SetSample(index, sample) => { + let i = index.as_int() as usize; + let old = state.mapped[i].clone(); + state.mapped[i] = sample; + Some(Self::SetSample(index, old)) + }, + Self::RecordBegin(index) => { + state.begin_recording(index.as_int() as usize); + None + }, + Self::RecordCancel => { + state.cancel_recording(); + None + }, + Self::RecordFinish => { + state.finish_recording(); + None + }, + _ => todo!() +}); diff --git a/tek/examples/midi_import.rs.fixme b/tek/examples/midi_import.rs.fixme index c422c86c..9aa894be 100644 --- a/tek/examples/midi_import.rs.fixme +++ b/tek/examples/midi_import.rs.fixme @@ -13,6 +13,6 @@ impl HasClips for ExampleClips { fn main () -> Usually<()> { let mut phrases = ExampleClips(vec![]); - MidiPoolCommand::Import(0, String::from("./example.mid")).execute(&mut phrases)?; + PoolClipCommand::Import(0, String::from("./example.mid")).execute(&mut phrases)?; Ok(()) } diff --git a/tek/src/control.rs b/tek/src/control.rs index 5b096d90..49a7268d 100644 --- a/tek/src/control.rs +++ b/tek/src/control.rs @@ -7,7 +7,7 @@ use GrooveboxCommand as GrvCmd; use ArrangerCommand as ArrCmd; use SamplerCommand as SmplCmd; use MidiEditCommand as EditCmd; -use MidiPoolCommand as PoolCmd; +use PoolClipCommand as PoolCmd; handle!(TuiIn: |self: App, input| Ok(None)); //handle!(TuiIn: |self: Sequencer, input|SequencerCommand::execute_with_state(self, input.event())); @@ -34,36 +34,28 @@ handle!(TuiIn: |self: App, input| Ok(None)); impl EdnCommand for AppCommand { fn from_edn <'a> (state: &App, head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { match (head, tail) { - (Key("clear"), [ ]) => - Self::Clear, - (Key("clip"), [a, b @ ..]) => - Self::Clip(ClipCommand::from_edn(state, &a.to_ref(), b)), - (Key("clock"), [a, b @ ..]) => - Self::Clock(ClockCommand::from_edn(state, &a.to_ref(), b)), - (Key("color"), [a ]) => - Self::Color(ItemPalette::default()), - (Key("compact"), [a ]) => - Self::Compact(true), - (Key("editor"), [a, b @ ..]) => - Self::Editor(MidiEditCommand::from_edn(state, &a.to_ref(), b)), - (Key("enqueue"), [a ]) => - Self::Enqueue(None), - (Key("history"), [a ]) => - Self::History(0), - (Key("pool"), [a, b @ ..]) => - Self::Pool(PoolCommand::from_edn(state, &a.to_ref(), b)), - (Key("sampler"), [a, b @ ..]) => - Self::Sampler(SamplerCommand::from_edn(state, &a.to_ref(), b)), - (Key("scene"), [a, b @ ..]) => - Self::Scene(SceneCommand::from_edn(state, &a.to_ref(), b)), - (Key("select"), [a ]) => - Self::Select(ArrangerSelection::Mix), - (Key("stop-all"), [ ]) => - Self::StopAll, - (Key("track"), [a, b @ ..]) => - Self::Track(TrackCommand::from_edn(state, &a.to_ref(), b)), - (Key("zoom"), [a ]) => - Self::Zoom(0), + (Key("clear"), []) => Self::Clear, + (Key("stop-all"), []) => Self::StopAll, + + (Key("color"), [a]) => Self::Color(ItemPalette::default()), + (Key("compact"), [a]) => Self::Compact(true), + (Key("enqueue"), [a]) => Self::Enqueue(None), + (Key("history"), [a]) => Self::History(0), + (Key("select"), [a]) => Self::Select(ArrangerSelection::Mix), + (Key("zoom"), [a]) => Self::Zoom(0), + + (Key("clock"), [a, b @ ..]) => Self::Clock(ClockCommand::from_edn(state, &a.to_ref(), b)), + (Key("track"), [a, b @ ..]) => Self::Track(TrackCommand::from_edn(state, &a.to_ref(), b)), + (Key("scene"), [a, b @ ..]) => Self::Scene(SceneCommand::from_edn(state, &a.to_ref(), b)), + (Key("clip"), [a, b @ ..]) => Self::Clip(ClipCommand::from_edn(state, &a.to_ref(), b)), + + (Key("pool"), [a, b @ ..]) if let Some(pool) = state.pool.as_ref() => + Self::Pool(PoolCommand::from_edn(pool, &a.to_ref(), b)), + (Key("editor"), [a, b @ ..]) if let Some(editor) = state.editor.as_ref() => + Self::Editor(MidiEditCommand::from_edn(editor, &a.to_ref(), b)), + (Key("sampler"), [a, b @ ..]) if let Some(sampler) = state.sampler.as_ref() => + Self::Sampler(SamplerCommand::from_edn(sampler, &a.to_ref(), b)), + _ => panic!(), } } @@ -343,7 +335,7 @@ command!(|self: TrackCommand, state: App|match self { _ => todo!("track command" //undo //}, //// reload clip in editor to update color - //PoolCommand::Clip(MidiPoolCommand::SetColor(index, _)) => { + //PoolCommand::Clip(PoolClipCommand::SetColor(index, _)) => { //let undo = cmd.delegate(&mut state.pool, Self::Pool)?; //state.editor.set_clip(state.pool.clip().as_ref()); //undo diff --git a/time/src/clock.rs b/time/src/clock.rs index 6f5865da..e0fdb977 100644 --- a/time/src/clock.rs +++ b/time/src/clock.rs @@ -34,12 +34,11 @@ pub enum ClockCommand { SetQuant(f64), SetSync(f64), } -impl ClockCommand { - pub fn from_edn <'a> (head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { +impl EdnCommand for ClockCommand { + fn from_edn <'a> (state: &T, head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { todo!() } } - impl Command for ClockCommand { fn execute (self, state: &mut T) -> Perhaps { use ClockCommand::*;