remove some old macros

This commit is contained in:
🪞👃🪞 2025-01-02 21:52:30 +01:00
parent a82f73d475
commit f1c7512cbb
18 changed files with 306 additions and 361 deletions

View file

@ -120,7 +120,7 @@ impl<T: Content<Tui> + Handle<Tui> + Sized + 'static> TuiRun<T> for Arc<RwLock<T
if ::crossterm::event::poll(poll).is_ok() {
let event = ::crossterm::event::read().unwrap();
match event {
key_pat!(Ctrl-KeyCode::Char('c')) => {
kpat!(Ctrl-KeyCode::Char('c')) => {
exited.store(true, Relaxed);
},
_ => {

View file

@ -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 {

View file

@ -108,10 +108,10 @@ impl Handle<Tui> for Demo<Tui> {
fn handle (&mut self, from: &TuiIn) -> Perhaps<bool> {
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 {

View file

@ -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

View file

@ -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)?);
};
}

View file

@ -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 {

View file

@ -248,16 +248,16 @@ pub trait FocusWrap<T> {
pub fn to_focus_command <T: Send + Sync> (input: &TuiIn) -> Option<FocusCommand<T>> {
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
})
}

View file

@ -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,

View file

@ -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 {

View file

@ -213,15 +213,15 @@ handle!(<Tui>|self: Mixer, engine|{
handle!(<Tui>|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!(<Tui>|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!(<Tui>|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))
},

View file

@ -200,29 +200,29 @@ fn draw_header (state: &Plugin, to: &mut TuiOut, x: u16, y: u16, w: u16) {
handle!(<Tui>|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!(<Tui>|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!(<Tui>|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()?)?);

View file

@ -117,42 +117,42 @@ fn to_phrases_command (state: &PoolModel, input: &Event) -> Option<PoolCommand>
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))

View file

@ -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 {

View file

@ -39,18 +39,18 @@ impl InputToCommand<Event, PoolModel> 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 {

View file

@ -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<Arc<RwLock<Sample>>>),
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);
}
}
}
}

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,71 @@
use crate::*;
handle!(<Tui>|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<Arc<RwLock<Sample>>>),
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!()
});

View file

@ -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!(<Tui>|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!()
});