From f1c7512cbbd4795d29dc16000d6860252eb92f58 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Thu, 2 Jan 2025 21:52:30 +0100 Subject: [PATCH] remove some old macros --- engine/src/tui.rs | 2 +- engine/src/tui/tui_input.rs | 27 +----- examples/demo.rs.fixme | 4 +- src/arranger/arranger_command.rs | 82 ++++++++-------- src/command.rs | 8 +- src/file.rs | 32 +++---- src/focus.rs | 20 ++-- src/lib.rs | 2 +- src/midi/midi_editor.rs | 4 +- src/mixer.rs | 10 +- src/plugin.rs | 14 +-- src/pool.rs | 26 ++--- src/pool/clip_length.rs | 12 +-- src/pool/clip_rename.rs | 8 +- src/sampler.rs | 159 ++----------------------------- src/sampler/sampler_audio.rs | 111 +++++++++++++++++++++ src/sampler/sampler_command.rs | 71 ++++++++++++++ src/sampler/sampler_tui.rs | 75 --------------- 18 files changed, 306 insertions(+), 361 deletions(-) create mode 100644 src/sampler/sampler_audio.rs create mode 100644 src/sampler/sampler_command.rs diff --git a/engine/src/tui.rs b/engine/src/tui.rs index 3bdb54ac..7158e9e6 100644 --- a/engine/src/tui.rs +++ b/engine/src/tui.rs @@ -120,7 +120,7 @@ impl + Handle + Sized + 'static> TuiRun for Arc { + kpat!(Ctrl-KeyCode::Char('c')) => { exited.store(true, Relaxed); }, _ => { diff --git a/engine/src/tui/tui_input.rs b/engine/src/tui/tui_input.rs index 4b029293..a15feeb3 100644 --- a/engine/src/tui/tui_input.rs +++ b/engine/src/tui/tui_input.rs @@ -61,20 +61,11 @@ pub const fn shift (event: Event) -> Event { event } -#[macro_export] macro_rules! key_pat { - (Ctrl-Alt-$code:pat) => { key_event_pat!($code, KeyModifiers::CONTROL | KeyModifiers::ALT) }; - (Ctrl-$code:pat) => { key_event_pat!($code, KeyModifiers::CONTROL) }; - (Alt-$code:pat) => { key_event_pat!($code, KeyModifiers::ALT) }; - (Shift-$code:pat) => { key_event_pat!($code, KeyModifiers::SHIFT) }; - ($code:pat) => { crossterm::event::Event::Key(KeyEvent { - code: $code, - modifiers: KeyModifiers::NONE, - kind: KeyEventKind::Press, - state: KeyEventState::NONE - }) }; -} - -#[macro_export] macro_rules! key_event_pat { +#[macro_export] macro_rules! kpat { + (Ctrl-Alt-$code:pat) => { kpat!($code, KeyModifiers::CONTROL | KeyModifiers::ALT) }; + (Ctrl-$code:pat) => { kpat!($code, KeyModifiers::CONTROL) }; + (Alt-$code:pat) => { kpat!($code, KeyModifiers::ALT) }; + (Shift-$code:pat) => { kpat!($code, KeyModifiers::SHIFT) }; ($code:pat) => { crossterm::event::Event::Key(KeyEvent { code: $code, @@ -102,14 +93,6 @@ pub const fn shift (event: Event) -> Event { ($code:expr) => { key_event_expr!($code) }; } -#[macro_export] macro_rules! key_expr { - (Ctrl-Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::CONTROL | KeyModifiers::ALT) }; - (Ctrl-$code:ident) => { key_event_expr!($code, KeyModifiers::CONTROL) }; - (Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::ALT) }; - (Shift-$code:ident) => { key_event_expr!($code, KeyModifiers::SHIFT) }; - ($code:ident) => { key_event_expr!($code) }; -} - #[macro_export] macro_rules! key_event_expr { ($code:expr, $modifiers: expr) => { crossterm::event::Event::Key(KeyEvent { diff --git a/examples/demo.rs.fixme b/examples/demo.rs.fixme index cfd7d7b0..ce3c6556 100644 --- a/examples/demo.rs.fixme +++ b/examples/demo.rs.fixme @@ -108,10 +108,10 @@ impl Handle for Demo { fn handle (&mut self, from: &TuiIn) -> Perhaps { use KeyCode::{PageUp, PageDown}; match from.event() { - key_expr!(PageUp) => { + kexp!(PageUp) => { self.index = (self.index + 1) % self.items.len(); }, - key_expr!(PageDown) => { + kexp!(PageDown) => { self.index = if self.index > 1 { self.index - 1 } else { diff --git a/src/arranger/arranger_command.rs b/src/arranger/arranger_command.rs index 2669fc54..523eb9d2 100644 --- a/src/arranger/arranger_command.rs +++ b/src/arranger/arranger_command.rs @@ -69,78 +69,78 @@ keymap!(KEYS_ARRANGER = |state: ArrangerTui, input: Event| ArrangerCommand { let s_len = state.scenes.len(); match state.selected() { Selected::Clip(t, s) => match input { - key_pat!(Char('g')) => Some(Cmd::Phrases(PoolCommand::Select(0))), - key_pat!(Char('q')) => Some(Cmd::Clip(Clip::Enqueue(t, s))), - key_pat!(Char(',')) => Some(Cmd::Clip(Clip::Put(t, s, None))), - key_pat!(Char('.')) => Some(Cmd::Clip(Clip::Put(t, s, None))), - key_pat!(Char('<')) => Some(Cmd::Clip(Clip::Put(t, s, None))), - key_pat!(Char('>')) => Some(Cmd::Clip(Clip::Put(t, s, None))), - key_pat!(Char('p')) => Some(Cmd::Clip(Clip::Put(t, s, Some(state.pool.phrase().clone())))), - key_pat!(Char('l')) => Some(Cmd::Clip(ArrangerClipCommand::SetLoop(t, s, false))), - key_pat!(Delete) => Some(Cmd::Clip(Clip::Put(t, s, None))), + kpat!(Char('g')) => Some(Cmd::Phrases(PoolCommand::Select(0))), + kpat!(Char('q')) => Some(Cmd::Clip(Clip::Enqueue(t, s))), + kpat!(Char(',')) => Some(Cmd::Clip(Clip::Put(t, s, None))), + kpat!(Char('.')) => Some(Cmd::Clip(Clip::Put(t, s, None))), + kpat!(Char('<')) => Some(Cmd::Clip(Clip::Put(t, s, None))), + kpat!(Char('>')) => Some(Cmd::Clip(Clip::Put(t, s, None))), + kpat!(Char('p')) => Some(Cmd::Clip(Clip::Put(t, s, Some(state.pool.phrase().clone())))), + kpat!(Char('l')) => Some(Cmd::Clip(ArrangerClipCommand::SetLoop(t, s, false))), + kpat!(Delete) => Some(Cmd::Clip(Clip::Put(t, s, None))), - key_pat!(Up) => Some(Cmd::Select( + kpat!(Up) => Some(Cmd::Select( if s > 0 { Selected::Clip(t, s - 1) } else { Selected::Track(t) })), - key_pat!(Down) => Some(Cmd::Select( + kpat!(Down) => Some(Cmd::Select( Selected::Clip(t, (s + 1).min(s_len.saturating_sub(1))))), - key_pat!(Left) => Some(Cmd::Select( + kpat!(Left) => Some(Cmd::Select( if t > 0 { Selected::Clip(t - 1, s) } else { Selected::Scene(s) })), - key_pat!(Right) => Some(Cmd::Select( + kpat!(Right) => Some(Cmd::Select( Selected::Clip((t + 1).min(t_len.saturating_sub(1)), s))), _ => None }, Selected::Scene(s) => match input { - key_pat!(Char(',')) => Some(Cmd::Scene(Scene::Swap(s, s - 1))), - key_pat!(Char('.')) => Some(Cmd::Scene(Scene::Swap(s, s + 1))), - key_pat!(Char('<')) => Some(Cmd::Scene(Scene::Swap(s, s - 1))), - key_pat!(Char('>')) => Some(Cmd::Scene(Scene::Swap(s, s + 1))), - key_pat!(Char('q')) => Some(Cmd::Scene(Scene::Enqueue(s))), - key_pat!(Delete) => Some(Cmd::Scene(Scene::Delete(s))), - key_pat!(Char('c')) => Some(Cmd::Scene(Scene::SetColor(s, ItemPalette::random()))), + kpat!(Char(',')) => Some(Cmd::Scene(Scene::Swap(s, s - 1))), + kpat!(Char('.')) => Some(Cmd::Scene(Scene::Swap(s, s + 1))), + kpat!(Char('<')) => Some(Cmd::Scene(Scene::Swap(s, s - 1))), + kpat!(Char('>')) => Some(Cmd::Scene(Scene::Swap(s, s + 1))), + kpat!(Char('q')) => Some(Cmd::Scene(Scene::Enqueue(s))), + kpat!(Delete) => Some(Cmd::Scene(Scene::Delete(s))), + kpat!(Char('c')) => Some(Cmd::Scene(Scene::SetColor(s, ItemPalette::random()))), - key_pat!(Up) => Some( + kpat!(Up) => Some( Cmd::Select(if s > 0 { Selected::Scene(s - 1) } else { Selected::Mix })), - key_pat!(Down) => Some( + kpat!(Down) => Some( Cmd::Select(Selected::Scene((s + 1).min(s_len.saturating_sub(1))))), - key_pat!(Left) => + kpat!(Left) => return None, - key_pat!(Right) => Some( + kpat!(Right) => Some( Cmd::Select(Selected::Clip(0, s))), _ => None }, Selected::Track(t) => match input { - key_pat!(Char(',')) => Some(Cmd::Track(Track::Swap(t, t - 1))), - key_pat!(Char('.')) => Some(Cmd::Track(Track::Swap(t, t + 1))), - key_pat!(Char('<')) => Some(Cmd::Track(Track::Swap(t, t - 1))), - key_pat!(Char('>')) => Some(Cmd::Track(Track::Swap(t, t + 1))), - key_pat!(Delete) => Some(Cmd::Track(Track::Delete(t))), - key_pat!(Char('c')) => Some(Cmd::Track(Track::SetColor(t, ItemPalette::random()))), + kpat!(Char(',')) => Some(Cmd::Track(Track::Swap(t, t - 1))), + kpat!(Char('.')) => Some(Cmd::Track(Track::Swap(t, t + 1))), + kpat!(Char('<')) => Some(Cmd::Track(Track::Swap(t, t - 1))), + kpat!(Char('>')) => Some(Cmd::Track(Track::Swap(t, t + 1))), + kpat!(Delete) => Some(Cmd::Track(Track::Delete(t))), + kpat!(Char('c')) => Some(Cmd::Track(Track::SetColor(t, ItemPalette::random()))), - key_pat!(Up) => + kpat!(Up) => return None, - key_pat!(Down) => Some( + kpat!(Down) => Some( Cmd::Select(Selected::Clip(t, 0))), - key_pat!(Left) => Some( + kpat!(Left) => Some( Cmd::Select(if t > 0 { Selected::Track(t - 1) } else { Selected::Mix })), - key_pat!(Right) => Some( + kpat!(Right) => Some( Cmd::Select(Selected::Track((t + 1).min(t_len.saturating_sub(1))))), _ => None }, Selected::Mix => match input { - key_pat!(Delete) => Some(Cmd::Clear), - key_pat!(Char('0')) => Some(Cmd::StopAll), - key_pat!(Char('c')) => Some(Cmd::Color(ItemPalette::random())), + kpat!(Delete) => Some(Cmd::Clear), + kpat!(Char('0')) => Some(Cmd::StopAll), + kpat!(Char('c')) => Some(Cmd::Color(ItemPalette::random())), - key_pat!(Up) => + kpat!(Up) => return None, - key_pat!(Down) => Some( + kpat!(Down) => Some( Cmd::Select(Selected::Scene(0))), - key_pat!(Left) => + kpat!(Left) => return None, - key_pat!(Right) => Some( + kpat!(Right) => Some( Cmd::Select(Selected::Track(0))), _ => None diff --git a/src/command.rs b/src/command.rs index 480f01ac..5a99fa9f 100644 --- a/src/command.rs +++ b/src/command.rs @@ -9,9 +9,7 @@ use crate::*; fallback: None, bindings: &[ $(($key, &|$state|Some($handler)),)* ] }; - input_to_command!($Command: |state: $State, input: $Input|{ - $KEYS.handle(state, input)? - }); + input_to_command!($Command: |state: $State, input: $Input|$KEYS.handle(state, input)?); }; ( $KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty @@ -22,9 +20,7 @@ use crate::*; fallback: Some(&|$state, $input|Some($default)), bindings: &[ $(($key, &|$state|Some($handler)),)* ] }; - input_to_command!($Command: |state: $State, input: $Input|{ - $KEYS.handle(state, input)? - }); + input_to_command!($Command: |state: $State, input: $Input|$KEYS.handle(state, input)?); }; } diff --git a/src/file.rs b/src/file.rs index 8d826eeb..f964e517 100644 --- a/src/file.rs +++ b/src/file.rs @@ -119,30 +119,30 @@ input_to_command!(FileBrowserCommand: |state: PoolModel, input: Event|{ use KeyCode::{Up, Down, Left, Right, Enter, Esc, Backspace, Char}; if let Some(PoolMode::Import(_index, browser)) = &state.mode { match input { - key_pat!(Up) => Select(browser.index.overflowing_sub(1).0 + kpat!(Up) => Select(browser.index.overflowing_sub(1).0 .min(browser.len().saturating_sub(1))), - key_pat!(Down) => Select(browser.index.saturating_add(1) + kpat!(Down) => Select(browser.index.saturating_add(1) % browser.len()), - key_pat!(Right) => Chdir(browser.cwd.clone()), - key_pat!(Left) => Chdir(browser.cwd.clone()), - key_pat!(Enter) => Confirm, - key_pat!(Char(_)) => { todo!() }, - key_pat!(Backspace) => { todo!() }, - key_pat!(Esc) => Cancel, + kpat!(Right) => Chdir(browser.cwd.clone()), + kpat!(Left) => Chdir(browser.cwd.clone()), + kpat!(Enter) => Confirm, + kpat!(Char(_)) => { todo!() }, + kpat!(Backspace) => { todo!() }, + kpat!(Esc) => Cancel, _ => return None } } else if let Some(PoolMode::Export(_index, browser)) = &state.mode { match input { - key_pat!(Up) => Select(browser.index.overflowing_sub(1).0 + kpat!(Up) => Select(browser.index.overflowing_sub(1).0 .min(browser.len())), - key_pat!(Down) => Select(browser.index.saturating_add(1) + kpat!(Down) => Select(browser.index.saturating_add(1) % browser.len()), - key_pat!(Right) => Chdir(browser.cwd.clone()), - key_pat!(Left) => Chdir(browser.cwd.clone()), - key_pat!(Enter) => Confirm, - key_pat!(Char(_)) => { todo!() }, - key_pat!(Backspace) => { todo!() }, - key_pat!(Esc) => Cancel, + kpat!(Right) => Chdir(browser.cwd.clone()), + kpat!(Left) => Chdir(browser.cwd.clone()), + kpat!(Enter) => Confirm, + kpat!(Char(_)) => { todo!() }, + kpat!(Backspace) => { todo!() }, + kpat!(Esc) => Cancel, _ => return None } } else { diff --git a/src/focus.rs b/src/focus.rs index 4ab3f390..eda38a41 100644 --- a/src/focus.rs +++ b/src/focus.rs @@ -248,16 +248,16 @@ pub trait FocusWrap { pub fn to_focus_command (input: &TuiIn) -> Option> { Some(match input.event() { - key_pat!(Tab) => FocusCommand::Next, - key_pat!(Shift-Tab) => FocusCommand::Prev, - key_pat!(BackTab) => FocusCommand::Prev, - key_pat!(Shift-BackTab) => FocusCommand::Prev, - key_pat!(Up) => FocusCommand::Up, - key_pat!(Down) => FocusCommand::Down, - key_pat!(Left) => FocusCommand::Left, - key_pat!(Right) => FocusCommand::Right, - key_pat!(Enter) => FocusCommand::Enter, - key_pat!(Esc) => FocusCommand::Exit, + kpat!(Tab) => FocusCommand::Next, + kpat!(Shift-Tab) => FocusCommand::Prev, + kpat!(BackTab) => FocusCommand::Prev, + kpat!(Shift-BackTab) => FocusCommand::Prev, + kpat!(Up) => FocusCommand::Up, + kpat!(Down) => FocusCommand::Down, + kpat!(Left) => FocusCommand::Left, + kpat!(Right) => FocusCommand::Right, + kpat!(Enter) => FocusCommand::Enter, + kpat!(Esc) => FocusCommand::Exit, _ => return None }) } diff --git a/src/lib.rs b/src/lib.rs index a94809fb..a60d3762 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ pub(crate) use ::tek_layout::{ Engine, Size, Area, Output, Content, Thunk, render, Input, Handle, handle, - kexp, key_pat, key_event_pat, key_event_expr, + kexp, kpat, tui::{ Tui, TuiIn, key, ctrl, shift, alt, diff --git a/src/midi/midi_editor.rs b/src/midi/midi_editor.rs index 82b89ba8..bab9a1fb 100644 --- a/src/midi/midi_editor.rs +++ b/src/midi/midi_editor.rs @@ -181,8 +181,8 @@ keymap!(KEYS_MIDI_EDITOR = |s: MidiEditor, _input: Event| MidiEditCommand { key(Char('.')) => SetNoteLength(Note::next(s.note_len())), key(Char('<')) => SetNoteLength(Note::prev(s.note_len())), key(Char('>')) => SetNoteLength(Note::next(s.note_len())), - //// TODO: key_pat!(Char('/')) => // toggle 3plet - //// TODO: key_pat!(Char('?')) => // toggle dotted + //// TODO: kpat!(Char('/')) => // toggle 3plet + //// TODO: kpat!(Char('?')) => // toggle dotted }); impl MidiEditor { diff --git a/src/mixer.rs b/src/mixer.rs index ea88ee18..2802705c 100644 --- a/src/mixer.rs +++ b/src/mixer.rs @@ -213,15 +213,15 @@ handle!(|self: Mixer, engine|{ handle!(|self:MixerTrack,from|{ match from.event() { //, NONE, "chain_cursor_up", "move cursor up", || { - key_pat!(KeyCode::Up) => { + kpat!(KeyCode::Up) => { Ok(Some(true)) }, // , NONE, "chain_cursor_down", "move cursor down", || { - key_pat!(KeyCode::Down) => { + kpat!(KeyCode::Down) => { Ok(Some(true)) }, // Left, NONE, "chain_cursor_left", "move cursor left", || { - key_pat!(KeyCode::Left) => { + kpat!(KeyCode::Left) => { //if let Some(track) = app.arranger.track_mut() { //track.device = track.device.saturating_sub(1); //return Ok(true) @@ -229,7 +229,7 @@ handle!(|self:MixerTrack,from|{ Ok(Some(true)) }, // , NONE, "chain_cursor_right", "move cursor right", || { - key_pat!(KeyCode::Right) => { + kpat!(KeyCode::Right) => { //if let Some(track) = app.arranger.track_mut() { //track.device = (track.device + 1).min(track.devices.len().saturating_sub(1)); //return Ok(true) @@ -237,7 +237,7 @@ handle!(|self:MixerTrack,from|{ Ok(Some(true)) }, // , NONE, "chain_mode_switch", "switch the display mode", || { - key_pat!(KeyCode::Char('`')) => { + kpat!(KeyCode::Char('`')) => { //app.chain_mode = !app.chain_mode; Ok(Some(true)) }, diff --git a/src/plugin.rs b/src/plugin.rs index bcec2c0f..763cd328 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -200,29 +200,29 @@ fn draw_header (state: &Plugin, to: &mut TuiOut, x: u16, y: u16, w: u16) { handle!(|self:Plugin, from|{ match from.event() { - key_pat!(KeyCode::Up) => { + kpat!(KeyCode::Up) => { self.selected = self.selected.saturating_sub(1); Ok(Some(true)) }, - key_pat!(KeyCode::Down) => { + kpat!(KeyCode::Down) => { self.selected = (self.selected + 1).min(match &self.plugin { Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, _ => unimplemented!() }); Ok(Some(true)) }, - key_pat!(KeyCode::PageUp) => { + kpat!(KeyCode::PageUp) => { self.selected = self.selected.saturating_sub(8); Ok(Some(true)) }, - key_pat!(KeyCode::PageDown) => { + kpat!(KeyCode::PageDown) => { self.selected = (self.selected + 10).min(match &self.plugin { Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, _ => unimplemented!() }); Ok(Some(true)) }, - key_pat!(KeyCode::Char(',')) => { + kpat!(KeyCode::Char(',')) => { match self.plugin.as_mut() { Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { let index = port_list[self.selected].index; @@ -234,7 +234,7 @@ handle!(|self:Plugin, from|{ } Ok(Some(true)) }, - key_pat!(KeyCode::Char('.')) => { + kpat!(KeyCode::Char('.')) => { match self.plugin.as_mut() { Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { let index = port_list[self.selected].index; @@ -246,7 +246,7 @@ handle!(|self:Plugin, from|{ } Ok(Some(true)) }, - key_pat!(KeyCode::Char('g')) => { + kpat!(KeyCode::Char('g')) => { match self.plugin { //Some(PluginKind::LV2(ref mut plugin)) => { //plugin.ui_thread = Some(run_lv2_ui(LV2PluginUI::new()?)?); diff --git a/src/pool.rs b/src/pool.rs index 08f1f528..8f456c3a 100644 --- a/src/pool.rs +++ b/src/pool.rs @@ -117,42 +117,42 @@ fn to_phrases_command (state: &PoolModel, input: &Event) -> Option let index = state.phrase_index(); let count = state.phrases().len(); Some(match input { - key_pat!(Char('n')) => Cmd::Rename(PhraseRenameCommand::Begin), - key_pat!(Char('t')) => Cmd::Length(PhraseLengthCommand::Begin), - key_pat!(Char('m')) => Cmd::Import(FileBrowserCommand::Begin), - key_pat!(Char('x')) => Cmd::Export(FileBrowserCommand::Begin), - key_pat!(Char('c')) => Cmd::Phrase(PhrasePoolCommand::SetColor(index, ItemColor::random())), - key_pat!(Char('[')) | key_pat!(Up) => Cmd::Select( + kpat!(Char('n')) => Cmd::Rename(PhraseRenameCommand::Begin), + kpat!(Char('t')) => Cmd::Length(PhraseLengthCommand::Begin), + kpat!(Char('m')) => Cmd::Import(FileBrowserCommand::Begin), + kpat!(Char('x')) => Cmd::Export(FileBrowserCommand::Begin), + kpat!(Char('c')) => Cmd::Phrase(PhrasePoolCommand::SetColor(index, ItemColor::random())), + kpat!(Char('[')) | kpat!(Up) => Cmd::Select( index.overflowing_sub(1).0.min(state.phrases().len() - 1) ), - key_pat!(Char(']')) | key_pat!(Down) => Cmd::Select( + kpat!(Char(']')) | kpat!(Down) => Cmd::Select( index.saturating_add(1) % state.phrases().len() ), - key_pat!(Char('<')) => if index > 1 { + kpat!(Char('<')) => if index > 1 { state.set_phrase_index(state.phrase_index().saturating_sub(1)); Cmd::Phrase(PhrasePoolCommand::Swap(index - 1, index)) } else { return None }, - key_pat!(Char('>')) => if index < count.saturating_sub(1) { + kpat!(Char('>')) => if index < count.saturating_sub(1) { state.set_phrase_index(state.phrase_index() + 1); Cmd::Phrase(PhrasePoolCommand::Swap(index + 1, index)) } else { return None }, - key_pat!(Delete) => if index > 0 { + kpat!(Delete) => if index > 0 { state.set_phrase_index(index.min(count.saturating_sub(1))); Cmd::Phrase(PhrasePoolCommand::Delete(index)) } else { return None }, - key_pat!(Char('a')) | key_pat!(Shift-Char('A')) => Cmd::Phrase(PhrasePoolCommand::Add(count, MidiClip::new( + kpat!(Char('a')) | kpat!(Shift-Char('A')) => Cmd::Phrase(PhrasePoolCommand::Add(count, MidiClip::new( String::from("Clip"), true, 4 * PPQ, None, Some(ItemPalette::random()) ))), - key_pat!(Char('i')) => Cmd::Phrase(PhrasePoolCommand::Add(index + 1, MidiClip::new( + kpat!(Char('i')) => Cmd::Phrase(PhrasePoolCommand::Add(index + 1, MidiClip::new( String::from("Clip"), true, 4 * PPQ, None, Some(ItemPalette::random()) ))), - key_pat!(Char('d')) | key_pat!(Shift-Char('D')) => { + kpat!(Char('d')) | kpat!(Shift-Char('D')) => { let mut phrase = state.phrases()[index].read().unwrap().duplicate(); phrase.color = ItemPalette::random_near(phrase.color, 0.25); Cmd::Phrase(PhrasePoolCommand::Add(index + 1, phrase)) diff --git a/src/pool/clip_length.rs b/src/pool/clip_length.rs index b278d6a0..c43f9741 100644 --- a/src/pool/clip_length.rs +++ b/src/pool/clip_length.rs @@ -130,12 +130,12 @@ command!(|self:PhraseLengthCommand,state:PoolModel|{ input_to_command!(PhraseLengthCommand: |state: PoolModel, input: Event|{ if let Some(PoolMode::Length(_, length, _)) = state.phrases_mode() { match input { - key_pat!(Up) => Self::Inc, - key_pat!(Down) => Self::Dec, - key_pat!(Right) => Self::Next, - key_pat!(Left) => Self::Prev, - key_pat!(Enter) => Self::Set(*length), - key_pat!(Esc) => Self::Cancel, + kpat!(Up) => Self::Inc, + kpat!(Down) => Self::Dec, + kpat!(Right) => Self::Next, + kpat!(Left) => Self::Prev, + kpat!(Enter) => Self::Set(*length), + kpat!(Esc) => Self::Cancel, _ => return None } } else { diff --git a/src/pool/clip_rename.rs b/src/pool/clip_rename.rs index d3cb8138..af9c2339 100644 --- a/src/pool/clip_rename.rs +++ b/src/pool/clip_rename.rs @@ -39,18 +39,18 @@ impl InputToCommand for PhraseRenameCommand { use KeyCode::{Char, Backspace, Enter, Esc}; if let Some(PoolMode::Rename(_, ref old_name)) = state.phrases_mode() { Some(match input { - key_pat!(Char(c)) => { + kpat!(Char(c)) => { let mut new_name = old_name.clone(); new_name.push(*c); Self::Set(new_name) }, - key_pat!(Backspace) => { + kpat!(Backspace) => { let mut new_name = old_name.clone(); new_name.pop(); Self::Set(new_name) }, - key_pat!(Enter) => Self::Confirm, - key_pat!(Esc) => Self::Cancel, + kpat!(Enter) => Self::Confirm, + kpat!(Esc) => Self::Cancel, _ => return None }) } else { diff --git a/src/sampler.rs b/src/sampler.rs index 259b1198..eded59f7 100644 --- a/src/sampler.rs +++ b/src/sampler.rs @@ -1,10 +1,12 @@ -mod sample; pub use self::sample::*; -mod voice; pub use self::voice::*; -mod sampler_tui; pub use self::sampler_tui::*; -mod sampler_status; pub use self::sampler_status::*; -mod sample_import; pub use self::sample_import::*; -mod sample_list; pub use self::sample_list::*; -mod sample_viewer; pub use self::sample_viewer::*; +mod sample; pub use self::sample::*; +mod sample_import; pub use self::sample_import::*; +mod sample_list; pub use self::sample_list::*; +mod sample_viewer; pub use self::sample_viewer::*; +mod sampler_audio; pub use self::sampler_audio::*; +mod sampler_command; pub use self::sampler_command::*; +mod sampler_status; pub use self::sampler_status::*; +mod sampler_tui; pub use self::sampler_tui::*; +mod voice; pub use self::voice::*; use crate::*; use KeyCode::Char; @@ -88,146 +90,3 @@ impl Sampler { } } } - -pub enum SamplerCommand { - RecordBegin(u7), - RecordCancel, - RecordFinish, - SetSample(u7, Option>>), - SetStart(u7, usize), - SetGain(f32), - NoteOn(u7, u7), - NoteOff(u7), -} - -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!() -}); - -audio!(|self: SamplerTui, client, scope|{ - SamplerAudio(&mut self.state).process(client, scope) -}); - -pub struct SamplerAudio<'a>(pub &'a mut Sampler); - -audio!(|self: SamplerAudio<'a>, _client, scope|{ - self.0.process_midi_in(scope); - self.0.clear_output_buffer(); - self.0.process_audio_out(scope); - self.0.write_output_buffer(scope); - self.0.process_audio_in(scope); - Control::Continue -}); - -impl Sampler { - - pub fn process_audio_in (&mut self, scope: &ProcessScope) { - let Sampler { audio_ins, input_meter, recording, .. } = self; - if audio_ins.len() != input_meter.len() { - *input_meter = vec![0.0;audio_ins.len()]; - } - if let Some((_, sample)) = recording { - let mut sample = sample.write().unwrap(); - if sample.channels.len() != audio_ins.len() { - panic!("channel count mismatch"); - } - let iterator = audio_ins.iter().zip(input_meter).zip(sample.channels.iter_mut()); - let mut length = 0; - for ((input, meter), channel) in iterator { - let slice = input.as_slice(scope); - length = length.max(slice.len()); - let total: f32 = slice.iter().map(|x|x.abs()).sum(); - let count = slice.len() as f32; - *meter = 10. * (total / count).log10(); - channel.extend_from_slice(slice); - } - sample.end += length; - } else { - for (input, meter) in audio_ins.iter().zip(input_meter) { - let slice = input.as_slice(scope); - let total: f32 = slice.iter().map(|x|x.abs()).sum(); - let count = slice.len() as f32; - *meter = 10. * (total / count).log10(); - } - } - } - - /// Create [Voice]s from [Sample]s in response to MIDI input. - pub fn process_midi_in (&mut self, scope: &ProcessScope) { - let Sampler { midi_in, mapped, voices, .. } = self; - for RawMidi { time, bytes } in midi_in.iter(scope) { - if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() { - match message { - MidiMessage::NoteOn { ref key, ref vel } => { - if let Some(ref sample) = mapped[key.as_int() as usize] { - voices.write().unwrap().push(Sample::play(sample, time as usize, vel)); - } - }, - MidiMessage::Controller { controller, value } => { - // TODO - } - _ => {} - } - } - } - } - - /// Zero the output buffer. - pub fn clear_output_buffer (&mut self) { - for buffer in self.buffer.iter_mut() { - buffer.fill(0.0); - } - } - - /// Mix all currently playing samples into the output. - pub fn process_audio_out (&mut self, scope: &ProcessScope) { - let Sampler { ref mut buffer, voices, output_gain, .. } = self; - let channel_count = buffer.len(); - voices.write().unwrap().retain_mut(|voice|{ - for index in 0..scope.n_frames() as usize { - if let Some(frame) = voice.next() { - for (channel, sample) in frame.iter().enumerate() { - // Averaging mixer: - //self.buffer[channel % channel_count][index] = ( - //(self.buffer[channel % channel_count][index] + sample * self.output_gain) / 2.0 - //); - buffer[channel % channel_count][index] += sample * *output_gain; - } - } else { - return false - } - } - true - }); - } - - /// Write output buffer to output ports. - pub fn write_output_buffer (&mut self, scope: &ProcessScope) { - let Sampler { ref mut audio_outs, buffer, .. } = self; - for (i, port) in audio_outs.iter_mut().enumerate() { - let buffer = &buffer[i]; - for (i, value) in port.as_mut_slice(scope).iter_mut().enumerate() { - *value = *buffer.get(i).unwrap_or(&0.0); - } - } - } - -} diff --git a/src/sampler/sampler_audio.rs b/src/sampler/sampler_audio.rs new file mode 100644 index 00000000..600622cf --- /dev/null +++ b/src/sampler/sampler_audio.rs @@ -0,0 +1,111 @@ +use crate::*; + +audio!(|self: SamplerTui, client, scope|{ + SamplerAudio(&mut self.state).process(client, scope) +}); + +pub struct SamplerAudio<'a>(pub &'a mut Sampler); + +audio!(|self: SamplerAudio<'a>, _client, scope|{ + self.0.process_midi_in(scope); + self.0.clear_output_buffer(); + self.0.process_audio_out(scope); + self.0.write_output_buffer(scope); + self.0.process_audio_in(scope); + Control::Continue +}); + +impl Sampler { + + pub fn process_audio_in (&mut self, scope: &ProcessScope) { + let Sampler { audio_ins, input_meter, recording, .. } = self; + if audio_ins.len() != input_meter.len() { + *input_meter = vec![0.0;audio_ins.len()]; + } + if let Some((_, sample)) = recording { + let mut sample = sample.write().unwrap(); + if sample.channels.len() != audio_ins.len() { + panic!("channel count mismatch"); + } + let iterator = audio_ins.iter().zip(input_meter).zip(sample.channels.iter_mut()); + let mut length = 0; + for ((input, meter), channel) in iterator { + let slice = input.as_slice(scope); + length = length.max(slice.len()); + let total: f32 = slice.iter().map(|x|x.abs()).sum(); + let count = slice.len() as f32; + *meter = 10. * (total / count).log10(); + channel.extend_from_slice(slice); + } + sample.end += length; + } else { + for (input, meter) in audio_ins.iter().zip(input_meter) { + let slice = input.as_slice(scope); + let total: f32 = slice.iter().map(|x|x.abs()).sum(); + let count = slice.len() as f32; + *meter = 10. * (total / count).log10(); + } + } + } + + /// Create [Voice]s from [Sample]s in response to MIDI input. + pub fn process_midi_in (&mut self, scope: &ProcessScope) { + let Sampler { midi_in, mapped, voices, .. } = self; + for RawMidi { time, bytes } in midi_in.iter(scope) { + if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() { + match message { + MidiMessage::NoteOn { ref key, ref vel } => { + if let Some(ref sample) = mapped[key.as_int() as usize] { + voices.write().unwrap().push(Sample::play(sample, time as usize, vel)); + } + }, + MidiMessage::Controller { controller, value } => { + // TODO + } + _ => {} + } + } + } + } + + /// Zero the output buffer. + pub fn clear_output_buffer (&mut self) { + for buffer in self.buffer.iter_mut() { + buffer.fill(0.0); + } + } + + /// Mix all currently playing samples into the output. + pub fn process_audio_out (&mut self, scope: &ProcessScope) { + let Sampler { ref mut buffer, voices, output_gain, .. } = self; + let channel_count = buffer.len(); + voices.write().unwrap().retain_mut(|voice|{ + for index in 0..scope.n_frames() as usize { + if let Some(frame) = voice.next() { + for (channel, sample) in frame.iter().enumerate() { + // Averaging mixer: + //self.buffer[channel % channel_count][index] = ( + //(self.buffer[channel % channel_count][index] + sample * self.output_gain) / 2.0 + //); + buffer[channel % channel_count][index] += sample * *output_gain; + } + } else { + return false + } + } + true + }); + } + + /// Write output buffer to output ports. + pub fn write_output_buffer (&mut self, scope: &ProcessScope) { + let Sampler { ref mut audio_outs, buffer, .. } = self; + for (i, port) in audio_outs.iter_mut().enumerate() { + let buffer = &buffer[i]; + for (i, value) in port.as_mut_slice(scope).iter_mut().enumerate() { + *value = *buffer.get(i).unwrap_or(&0.0); + } + } + } + +} diff --git a/src/sampler/sampler_command.rs b/src/sampler/sampler_command.rs new file mode 100644 index 00000000..26d4e4aa --- /dev/null +++ b/src/sampler/sampler_command.rs @@ -0,0 +1,71 @@ +use crate::*; + +handle!(|self: SamplerTui, input|SamplerTuiCommand::execute_with_state(self, input.event())); + +pub enum SamplerTuiCommand { + Import(FileBrowserCommand), + Select(usize), + Sample(SamplerCommand), +} + +pub enum SamplerCommand { + RecordBegin(u7), + RecordCancel, + RecordFinish, + SetSample(u7, Option>>), + SetStart(u7, usize), + SetGain(f32), + NoteOn(u7, u7), + NoteOff(u7), +} + +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/src/sampler/sampler_tui.rs b/src/sampler/sampler_tui.rs index dc12b399..66fb2d12 100644 --- a/src/sampler/sampler_tui.rs +++ b/src/sampler/sampler_tui.rs @@ -31,19 +31,6 @@ impl SamplerTui { } } -//from_jack!(|jack|SamplerTui{ - //Self { - //cursor: (0, 0), - //editing: None, - //mode: None, - //size: Measure::new(), - //note_lo: 36.into(), - //note_pt: 36.into(), - //color: ItemPalette::from(Color::Rgb(64, 128, 32)), - //state: Sampler::new(jack, &"sampler", &[], &[&[], &[]], &[&[], &[]])?, - //} -//}); - render!(Tui: (self: SamplerTui) => { let keys_width = 5; let keys = move||"";//SamplerKeys(self); @@ -102,65 +89,3 @@ pub enum SamplerMode { // Load sample from path Import(usize, FileBrowser), } - -handle!(|self: SamplerTui, input|SamplerTuiCommand::execute_with_state(self, input.event())); - -pub enum SamplerTuiCommand { - Import(FileBrowserCommand), - SelectNote(usize), - SelectField(usize), - Sample(SamplerCommand), -} - -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 - key_pat!(Shift-Char('L')) => { - Self::Import(FileBrowserCommand::Begin) - }, - key_pat!(KeyCode::Up) => { - Self::SelectNote(state.note_point().overflowing_add(1).0.min(127)) - }, - key_pat!(KeyCode::Down) => { - Self::SelectNote(state.note_point().overflowing_sub(1).0.min(127)) - }, - _ => return None - } - //key_pat!(KeyCode::Char('p')) => if let Some(sample) = self.sample() { - //voices.write().unwrap().push(Sample::play(sample, 0, &100.into())); - //}, - //key_pat!(KeyCode::Char('a')) => { - //let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![]))); - //self.mode = None;//Some(Exit::boxed(AddSampleModal::new(&sample, &voices)?)); - //unmapped.push(sample); - //}, - //key_pat!(KeyCode::Char('r')) => if let Some(sample) = self.sample() { - //self.mode = None;//Some(Exit::boxed(AddSampleModal::new(&sample, &voices)?)); - //}, - //key_pat!(KeyCode::Enter) => if let Some(sample) = self.sample() { - //self.editing = Some(sample.clone()); - //}, - //_ => { - //return Ok(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::SelectNote(index) => { - let old = state.note_point(); - state.set_note_point(index); - Some(Self::SelectNote(old)) - }, - Self::Sample(cmd) => cmd.execute(&mut state.state)?.map(Self::Sample), - _ => todo!() -});