mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 04:36:45 +01:00
big ass refactor (rip client)
This commit is contained in:
parent
94c1f83ef2
commit
8c3cf53c67
56 changed files with 2232 additions and 1891 deletions
72
src/control/chain.rs
Normal file
72
src/control/chain.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
use crate::{core::*, model::*};
|
||||
use super::focus::*;
|
||||
|
||||
pub fn handle (state: &mut Chain, event: &AppEvent) -> Usually<bool> {
|
||||
Ok(handle_focus(state, event, keymap!(Chain {
|
||||
[Up, NONE, "focus_up", "focus row above",
|
||||
|s: &mut Chain|s.handle_focus(&FocusEvent::Backward)],
|
||||
[Down, NONE, "focus_down", "focus row below",
|
||||
|s: &mut Chain|s.handle_focus(&FocusEvent::Forward)],
|
||||
[Enter, NONE, "focus_down", "focus row below",
|
||||
|s: &mut Chain|s.handle_focus(&FocusEvent::Inward)],
|
||||
[Esc, NONE, "focus_down", "focus row below",
|
||||
|s: &mut Chain|s.handle_focus(&FocusEvent::Outward)],
|
||||
}))? || handle_keymap(state, event, keymap!(Chain {
|
||||
[Char('a'), NONE, "add_device", "add a device", add_device]
|
||||
}))?)
|
||||
}
|
||||
|
||||
fn add_device (state: &mut Chain) -> Usually<bool> {
|
||||
state.adding = true;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
impl Focus for Chain {
|
||||
fn unfocus (&mut self) {
|
||||
self.focused = false
|
||||
}
|
||||
fn focused (&self) -> Option<&Box<dyn Device>> {
|
||||
match self.focused {
|
||||
true => self.items.get(self.focus),
|
||||
false => None
|
||||
}
|
||||
}
|
||||
fn focused_mut (&mut self) -> Option<&mut Box<dyn Device>> {
|
||||
match self.focused {
|
||||
true => self.items.get_mut(self.focus),
|
||||
false => None
|
||||
}
|
||||
}
|
||||
fn handle_focus (&mut self, event: &FocusEvent) -> Usually<bool> {
|
||||
Ok(match event {
|
||||
FocusEvent::Backward => {
|
||||
if self.focus == 0 {
|
||||
self.focus = self.items.len();
|
||||
}
|
||||
self.focus = self.focus - 1;
|
||||
true
|
||||
},
|
||||
FocusEvent::Forward => {
|
||||
self.focus = self.focus + 1;
|
||||
if self.focus >= self.items.len() {
|
||||
self.focus = 0;
|
||||
}
|
||||
true
|
||||
},
|
||||
FocusEvent::Inward => {
|
||||
self.focused = true;
|
||||
self.items[self.focus].handle(&AppEvent::Focus)?;
|
||||
true
|
||||
},
|
||||
FocusEvent::Outward => {
|
||||
if self.focused {
|
||||
self.focused = false;
|
||||
self.items[self.focus].handle(&AppEvent::Blur)?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
189
src/control/focus.rs
Normal file
189
src/control/focus.rs
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
use crate::{core::*, view::*};
|
||||
|
||||
pub trait Focus {
|
||||
fn unfocus (&mut self);
|
||||
fn focused (&self) -> Option<&Box<dyn Device>>;
|
||||
fn focused_mut (&mut self) -> Option<&mut Box<dyn Device>>;
|
||||
fn handle_focus (&mut self, event: &FocusEvent) -> Usually<bool>;
|
||||
}
|
||||
|
||||
pub enum FocusEvent { Forward, Backward, Inward, Outward, }
|
||||
|
||||
pub fn handle_focus <T: Focus> (
|
||||
state: &mut T,
|
||||
event: &AppEvent,
|
||||
keymap: &[KeyBinding<T>]
|
||||
) -> Usually<bool> {
|
||||
let handled = if let Some(focused) = state.focused_mut() {
|
||||
focused.handle(event)
|
||||
} else {
|
||||
Ok(false)
|
||||
};
|
||||
return Ok(handled? || handle_keymap(
|
||||
state, event, keymap
|
||||
)?)
|
||||
}
|
||||
|
||||
pub struct FocusColumn(pub Option<usize>, pub Column);
|
||||
|
||||
impl Handle for FocusColumn {
|
||||
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
||||
handle_focus(self, event, KEYMAP_FOCUS_COLUMN)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for FocusColumn {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let (rect, _rects) = Column::draw(buf, area, self.1.0.as_ref(), 0)?;
|
||||
//if i == self.focus {
|
||||
//if self.focused {
|
||||
//draw_box_styled(buf, result, Some(Style::default().white().not_dim()))
|
||||
//} else {
|
||||
//draw_box_styled_dotted(buf, result, Some(Style::default().white().dim()))
|
||||
//};
|
||||
//};
|
||||
Ok(rect)
|
||||
}
|
||||
}
|
||||
|
||||
const KEYMAP_FOCUS_COLUMN: &'static [KeyBinding<FocusColumn>] = keymap!(FocusColumn {
|
||||
[Up, NONE, "focus_up", "focus row above",
|
||||
|s: &mut FocusColumn|s.handle_focus(&FocusEvent::Backward)],
|
||||
[Down, NONE, "focus_down", "focus row below",
|
||||
|s: &mut FocusColumn|s.handle_focus(&FocusEvent::Forward)],
|
||||
[Enter, NONE, "focus_down", "focus row below",
|
||||
|s: &mut FocusColumn|s.handle_focus(&FocusEvent::Inward)],
|
||||
[Esc, NONE, "focus_down", "focus row below",
|
||||
|s: &mut FocusColumn|s.handle_focus(&FocusEvent::Outward)]
|
||||
});
|
||||
|
||||
impl Focus for FocusColumn {
|
||||
fn unfocus (&mut self) {
|
||||
self.0 = None
|
||||
}
|
||||
fn focused (&self) -> Option<&Box<dyn Device>> {
|
||||
self.0.map(|index|self.1.0.get(index))?
|
||||
}
|
||||
fn focused_mut (&mut self) -> Option<&mut Box<dyn Device>> {
|
||||
self.0.map(|index|self.1.0.get_mut(index))?
|
||||
}
|
||||
fn handle_focus (&mut self, event: &FocusEvent) -> Usually<bool> {
|
||||
Ok(match event {
|
||||
FocusEvent::Backward => match self.0 {
|
||||
Some(i) => {
|
||||
self.0 = Some(if i == 0 {
|
||||
self.1.0.len() - 1
|
||||
} else {
|
||||
i - 1
|
||||
});
|
||||
true
|
||||
},
|
||||
_ => false
|
||||
},
|
||||
FocusEvent::Forward => match self.0 {
|
||||
Some(i) => {
|
||||
self.0 = Some(if i >= self.1.0.len() {
|
||||
0
|
||||
} else {
|
||||
i + 1
|
||||
});
|
||||
true
|
||||
},
|
||||
_ => false
|
||||
},
|
||||
FocusEvent::Inward => match self.0 {
|
||||
None => {
|
||||
self.0 = Some(0);
|
||||
true
|
||||
},
|
||||
_ => false
|
||||
},
|
||||
FocusEvent::Outward => match self.0 {
|
||||
Some(_i) => {
|
||||
self.0 = None;
|
||||
true
|
||||
},
|
||||
_ => false
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FocusRow(pub Option<usize>, pub Row);
|
||||
|
||||
impl Handle for FocusRow {
|
||||
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
||||
handle_focus(self, event, KEYMAP_FOCUS_ROW)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for FocusRow {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let (rect, _rects) = Row::draw(buf, area, &self.1.0, 0)?;
|
||||
Ok(rect)
|
||||
}
|
||||
}
|
||||
|
||||
const KEYMAP_FOCUS_ROW: &'static [KeyBinding<FocusRow>] = keymap!(FocusRow {
|
||||
[Left, NONE, "focus_up", "focus row above",
|
||||
|s: &mut FocusRow|s.handle_focus(&FocusEvent::Backward)],
|
||||
[Right, NONE, "focus_down", "focus row below",
|
||||
|s: &mut FocusRow|s.handle_focus(&FocusEvent::Forward)],
|
||||
[Enter, NONE, "focus_down", "focus row below",
|
||||
|s: &mut FocusRow|s.handle_focus(&FocusEvent::Inward)],
|
||||
[Esc, NONE, "focus_down", "focus row below",
|
||||
|s: &mut FocusRow|s.handle_focus(&FocusEvent::Outward)]
|
||||
});
|
||||
|
||||
impl Focus for FocusRow {
|
||||
fn unfocus (&mut self) {
|
||||
self.0 = None
|
||||
}
|
||||
fn focused (&self) -> Option<&Box<dyn Device>> {
|
||||
self.0.map(|index|self.1.0.get(index))?
|
||||
}
|
||||
fn focused_mut (&mut self) -> Option<&mut Box<dyn Device>> {
|
||||
self.0.map(|index|self.1.0.get_mut(index))?
|
||||
}
|
||||
fn handle_focus (&mut self, event: &FocusEvent) -> Usually<bool> {
|
||||
Ok(match event {
|
||||
FocusEvent::Backward => match self.0 {
|
||||
Some(i) => {
|
||||
self.0 = Some(if i == 0 {
|
||||
self.1.0.len() - 1
|
||||
} else {
|
||||
i - 1
|
||||
});
|
||||
true
|
||||
},
|
||||
_ => false
|
||||
},
|
||||
FocusEvent::Forward => match self.0 {
|
||||
Some(i) => {
|
||||
self.0 = Some(if i >= self.1.0.len() {
|
||||
0
|
||||
} else {
|
||||
i + 1
|
||||
});
|
||||
true
|
||||
},
|
||||
_ => false
|
||||
},
|
||||
FocusEvent::Inward => match self.0 {
|
||||
None => {
|
||||
self.0 = Some(0);
|
||||
true
|
||||
},
|
||||
_ => false
|
||||
},
|
||||
FocusEvent::Outward => match self.0 {
|
||||
Some(_i) => {
|
||||
self.0 = None;
|
||||
true
|
||||
},
|
||||
_ => false
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
236
src/control/launcher.rs
Normal file
236
src/control/launcher.rs
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
use crate::{core::*,model::*};
|
||||
|
||||
pub fn handle (state: &mut Launcher, event: &AppEvent) -> Usually<bool> {
|
||||
Ok(handle_keymap(state, event, KEYMAP)? || match state.view {
|
||||
LauncherMode::Tracks => {
|
||||
handle_keymap(state, event, KEYMAP_TRACKS)?
|
||||
},
|
||||
LauncherMode::Sequencer => {
|
||||
let i = state.col().saturating_sub(1);
|
||||
if let Some(track) = state.tracks.get_mut(i) {
|
||||
crate::control::sequencer::handle(&mut track.sequencer, event)?
|
||||
} else {
|
||||
true
|
||||
}
|
||||
},
|
||||
LauncherMode::Chains => {
|
||||
let i = state.col().saturating_sub(1);
|
||||
if let Some(track) = state.tracks.get_mut(i) {
|
||||
crate::control::chain::handle(&mut track.chain, event)?
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
pub const KEYMAP: &'static [KeyBinding<Launcher>] = keymap!(Launcher {
|
||||
[F(1), NONE, "toggle_help", "toggle help", toggle_help],
|
||||
[Tab, SHIFT, "focus_prev", "focus previous area", focus_prev],
|
||||
[Tab, NONE, "focus_next", "focus next area", focus_next],
|
||||
[Char(' '), NONE, "play_toggle", "play or pause", play_toggle],
|
||||
[Char('r'), NONE, "record_toggle", "toggle recording", record_toggle],
|
||||
[Char('d'), NONE, "overdub_toggle", "toggle overdub", overdub_toggle],
|
||||
[Char('m'), NONE, "monitor_toggle", "toggle input monitoring", monitor_toggle],
|
||||
[Char('r'), CONTROL, "rename", "rename current element", rename],
|
||||
[Char('t'), CONTROL, "add_track", "add a new track", add_track],
|
||||
//[Char(' '), SHIFT, "play_start", "play from start", play_start],
|
||||
});
|
||||
pub const KEYMAP_TRACKS: &'static [KeyBinding<Launcher>] = keymap!(Launcher {
|
||||
[Up, NONE, "cursor_up", "move cursor up", cursor_up],
|
||||
[Down, NONE, "cursor_down", "move cursor down", cursor_down],
|
||||
[Left, NONE, "cursor_left", "move cursor left", cursor_left],
|
||||
[Right, NONE, "cursor_right", "move cursor right", cursor_right],
|
||||
[Char('.'), NONE, "clip_next", "set clip to next phrase", clip_next],
|
||||
[Char(','), NONE, "clip_prev", "set clip to last phrase", clip_prev],
|
||||
[Delete, CONTROL, "delete_track", "delete track", delete_track],
|
||||
[Char('d'), CONTROL, "duplicate", "duplicate scene or track", duplicate],
|
||||
[Enter, NONE, "activate", "activate item at cursor", activate],
|
||||
});
|
||||
fn duplicate (_: &mut Launcher) -> Usually<bool> {
|
||||
unimplemented!();
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn activate (_: &mut Launcher) -> Usually<bool> {
|
||||
unimplemented!();
|
||||
//if let (
|
||||
//Some((_scene_id, scene)),
|
||||
//Some((track_id, track)),
|
||||
//) = (state.scene_mut(), state.track_mut()) {
|
||||
//// Launch clip
|
||||
//if let Some(phrase_id) = scene.clips.get(track_id) {
|
||||
//track.sequencer.sequence = *phrase_id;
|
||||
//}
|
||||
//if state.playing == TransportState::Stopped {
|
||||
//state.transport.start()?;
|
||||
//state.playing = TransportState::Starting;
|
||||
//}
|
||||
//} else if let Some((_scene_id, scene)) = state.scene() {
|
||||
//// Launch scene
|
||||
//for (track_id, track) in state.tracks.iter().enumerate() {
|
||||
//if let Some(phrase_id) = scene.clips.get(track_id) {
|
||||
//track.sequencer.sequence = *phrase_id;
|
||||
//}
|
||||
//}
|
||||
//if state.playing == TransportState::Stopped {
|
||||
//state.transport.start()?;
|
||||
//state.playing = TransportState::Starting;
|
||||
//}
|
||||
//} else if let Some((_track_id, _track)) = state.track() {
|
||||
//// Rename track?
|
||||
//}
|
||||
|
||||
//let track = state.active_track().unwrap();
|
||||
//let scene = state.active_scene();
|
||||
//if state.cursor.1 >= 2 {
|
||||
//if let Some(Some(index)) = scene.clips.get(state.cursor.1 - 2) {
|
||||
//track.enqueue(index)
|
||||
//} else {
|
||||
//}
|
||||
//}
|
||||
//if state.cursor.0 >= 1 {
|
||||
//let sequencer = state.tracks.get_mut(state.cursor.0 - 1);
|
||||
//if state.cursor.1 >= 2 {
|
||||
//let scene = state.scenes.get_mut(state.cursor.1 - 2);
|
||||
//if let Some(index) = scene.get(state.cursor.0 - 1) {
|
||||
//let phrase = sequencer.phrases.get(index);
|
||||
//} else {
|
||||
//let index = sequencer.phrases.len();
|
||||
//let phrase = Phrase::new(&format!("Phrase#{index}"));
|
||||
//sequencer.phrases.push(phrase);
|
||||
//scene[state.cursor.0 - 1] = Some(index);
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn rename (_: &mut Launcher) -> Usually<bool> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn add_track (state: &mut Launcher) -> Usually<bool> {
|
||||
let name = format!("Track {}", state.tracks.len() + 1);
|
||||
state.tracks.push(Track::new(&name, &state.timebase, None, None)?);
|
||||
state.cursor.0 = state.tracks.len();
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn delete_track (state: &mut Launcher) -> Usually<bool> {
|
||||
if state.tracks.len() > 0 && state.cursor.0 >= 1 {
|
||||
state.tracks.remove(state.cursor.0 - 1);
|
||||
state.cursor.0 = state.cursor.0.min(state.tracks.len());
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn cursor_up (state: &mut Launcher) -> Usually<bool> {
|
||||
state.dec_row();
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn cursor_down (state: &mut Launcher) -> Usually<bool> {
|
||||
state.inc_row();
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn cursor_left (state: &mut Launcher) -> Usually<bool> {
|
||||
state.dec_col();
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn cursor_right (state: &mut Launcher) -> Usually<bool> {
|
||||
state.inc_col();
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn toggle_help (state: &mut Launcher) -> Usually<bool> {
|
||||
state.show_help = !state.show_help;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn focus_next (state: &mut Launcher) -> Usually<bool> {
|
||||
match state.view {
|
||||
LauncherMode::Tracks => { state.view = LauncherMode::Sequencer; },
|
||||
LauncherMode::Sequencer => { state.view = LauncherMode::Chains; },
|
||||
LauncherMode::Chains => { state.view = LauncherMode::Tracks; },
|
||||
};
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn focus_prev (state: &mut Launcher) -> Usually<bool> {
|
||||
match state.view {
|
||||
LauncherMode::Tracks => { state.view = LauncherMode::Chains; },
|
||||
LauncherMode::Chains => { state.view = LauncherMode::Sequencer; },
|
||||
LauncherMode::Sequencer => { state.view = LauncherMode::Tracks; },
|
||||
};
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn clip_next (state: &mut Launcher) -> Usually<bool> {
|
||||
if state.cursor.0 >= 1 && state.cursor.1 >= 1 {
|
||||
let scene_id = state.cursor.1 - 1;
|
||||
let clip_id = state.cursor.0 - 1;
|
||||
let scene = &mut state.scenes[scene_id];
|
||||
scene.clips[clip_id] = match scene.clips[clip_id] {
|
||||
None => Some(0),
|
||||
Some(i) => if i >= state.tracks[clip_id].sequencer.phrases.len().saturating_sub(1) {
|
||||
None
|
||||
} else {
|
||||
Some(i + 1)
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn clip_prev (state: &mut Launcher) -> Usually<bool> {
|
||||
if state.cursor.0 >= 1 && state.cursor.1 >= 1 {
|
||||
let scene_id = state.cursor.1 - 1;
|
||||
let clip_id = state.cursor.0 - 1;
|
||||
let scene = &mut state.scenes[scene_id];
|
||||
scene.clips[clip_id] = match scene.clips[clip_id] {
|
||||
None => Some(state.tracks[clip_id].sequencer.phrases.len().saturating_sub(1)),
|
||||
Some(i) => if i == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(i - 1)
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn play_toggle (s: &mut Launcher) -> Usually<bool> {
|
||||
s.playing = match s.playing {
|
||||
TransportState::Stopped => {
|
||||
s.transport.start()?;
|
||||
TransportState::Starting
|
||||
},
|
||||
_ => {
|
||||
s.transport.stop()?;
|
||||
s.transport.locate(0)?;
|
||||
TransportState::Stopped
|
||||
},
|
||||
};
|
||||
Ok(true)
|
||||
}
|
||||
//fn play_start (_: &mut Launcher) -> Usually<bool> {
|
||||
//unimplemented!()
|
||||
//}
|
||||
|
||||
fn record_toggle (s: &mut Launcher) -> Usually<bool> {
|
||||
s.sequencer_mut().map(|s|s.recording = !s.recording);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn overdub_toggle (s: &mut Launcher) -> Usually<bool> {
|
||||
s.sequencer_mut().map(|s|s.overdub = !s.overdub);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn monitor_toggle (s: &mut Launcher) -> Usually<bool> {
|
||||
s.sequencer_mut().map(|s|s.monitoring = !s.monitoring);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
59
src/control/mixer.rs
Normal file
59
src/control/mixer.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use crate::{core::*, model::*};
|
||||
|
||||
pub fn handle (state: &mut Mixer, event: &AppEvent) -> Usually<bool> {
|
||||
if let AppEvent::Input(crossterm::event::Event::Key(event)) = event {
|
||||
|
||||
match event.code {
|
||||
//KeyCode::Char('c') => {
|
||||
//if event.modifiers == KeyModifiers::CONTROL {
|
||||
//state.exit();
|
||||
//}
|
||||
//},
|
||||
KeyCode::Down => {
|
||||
state.selected_track = (state.selected_track + 1) % state.tracks.len();
|
||||
println!("{}", state.selected_track);
|
||||
return Ok(true)
|
||||
},
|
||||
KeyCode::Up => {
|
||||
if state.selected_track == 0 {
|
||||
state.selected_track = state.tracks.len() - 1;
|
||||
} else {
|
||||
state.selected_track = state.selected_track - 1;
|
||||
}
|
||||
println!("{}", state.selected_track);
|
||||
return Ok(true)
|
||||
},
|
||||
KeyCode::Left => {
|
||||
if state.selected_column == 0 {
|
||||
state.selected_column = 6
|
||||
} else {
|
||||
state.selected_column = state.selected_column - 1;
|
||||
}
|
||||
return Ok(true)
|
||||
},
|
||||
KeyCode::Right => {
|
||||
if state.selected_column == 6 {
|
||||
state.selected_column = 0
|
||||
} else {
|
||||
state.selected_column = state.selected_column + 1;
|
||||
}
|
||||
return Ok(true)
|
||||
},
|
||||
_ => {
|
||||
println!("\n{event:?}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - Meters: propagate clipping:
|
||||
// - If one stage clips, all stages after it are marked red
|
||||
// - If one track clips, all tracks that feed from it are marked red?
|
||||
|
||||
pub const ACTIONS: [(&'static str, &'static str);2] = [
|
||||
("+/-", "Adjust"),
|
||||
("Ins/Del", "Add/remove track"),
|
||||
];
|
||||
69
src/control/plugin.rs
Normal file
69
src/control/plugin.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
use crate::{core::*, model::*};
|
||||
|
||||
pub fn handle (s: &mut Plugin, event: &AppEvent) -> Usually<bool> {
|
||||
handle_keymap(s, event, keymap!(Plugin {
|
||||
|
||||
[Up, NONE, "cursor_up", "move cursor up",
|
||||
|s: &mut Plugin|{
|
||||
if s.selected > 0 {
|
||||
s.selected = s.selected - 1
|
||||
} else {
|
||||
s.selected = match &s.plugin {
|
||||
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
|
||||
_ => 0
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}],
|
||||
|
||||
[Down, NONE, "cursor_down", "move cursor down",
|
||||
|s: &mut Plugin|{
|
||||
s.selected = s.selected + 1;
|
||||
match &s.plugin {
|
||||
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => {
|
||||
if s.selected >= port_list.len() {
|
||||
s.selected = 0;
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
Ok(true)
|
||||
}],
|
||||
|
||||
[Char(','), NONE, "decrement", "decrement value",
|
||||
|s: &mut Plugin|{
|
||||
match s.plugin.as_mut() {
|
||||
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
|
||||
let index = port_list[s.selected].index;
|
||||
if let Some(value) = instance.control_input(index) {
|
||||
instance.set_control_input(index, value - 0.01);
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
Ok(true)
|
||||
}],
|
||||
|
||||
[Char('.'), NONE, "increment", "increment value",
|
||||
|s: &mut Plugin|{
|
||||
match s.plugin.as_mut() {
|
||||
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
|
||||
let index = port_list[s.selected].index;
|
||||
if let Some(value) = instance.control_input(index) {
|
||||
instance.set_control_input(index, value + 0.01);
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
Ok(true)
|
||||
}],
|
||||
|
||||
[Char('m'), NONE, "toggle_midi_map", "toggle midi map mode",
|
||||
|s: &mut Plugin|{
|
||||
s.mapping = !s.mapping;
|
||||
Ok(true)
|
||||
}]
|
||||
|
||||
}))
|
||||
}
|
||||
|
||||
45
src/control/sampler.rs
Normal file
45
src/control/sampler.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use crate::{core::*, model::*};
|
||||
|
||||
pub fn handle (state: &mut Sampler, event: &AppEvent) -> Usually<bool> {
|
||||
Ok(handle_keymap(state, event, KEYMAP)?)
|
||||
}
|
||||
|
||||
pub const KEYMAP: &'static [KeyBinding<Sampler>] = keymap!(Sampler {
|
||||
[Up, NONE, "cursor_up", "move cursor up", cursor_up],
|
||||
[Down, NONE, "cursor_down", "move cursor down", cursor_down],
|
||||
[Char('t'), NONE, "trigger", "play current sample", trigger],
|
||||
[Enter, NONE, "select", "select item under cursor", select],
|
||||
});
|
||||
|
||||
fn cursor_up (state: &mut Sampler) -> Usually<bool> {
|
||||
state.cursor.0 = if state.cursor.0 == 0 {
|
||||
state.samples.len() - 1
|
||||
} else {
|
||||
state.cursor.0 - 1
|
||||
};
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn cursor_down (state: &mut Sampler) -> Usually<bool> {
|
||||
state.cursor.0 = (state.cursor.0 + 1) % state.samples.len();
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn trigger (state: &mut Sampler) -> Usually<bool> {
|
||||
for (i, sample) in state.samples.values().enumerate() {
|
||||
if i == state.cursor.0 {
|
||||
state.voices.push(sample.play(0))
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn select (state: &mut Sampler) -> Usually<bool> {
|
||||
for (i, _sample) in state.samples.values().enumerate() {
|
||||
if i == state.cursor.0 {
|
||||
//state.voices.push(sample.play(0))
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
222
src/control/sequencer.rs
Normal file
222
src/control/sequencer.rs
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
use crate::{core::*, model::*};
|
||||
|
||||
pub fn handle (state: &mut Sequencer, event: &AppEvent) -> Usually<bool> {
|
||||
handle_keymap(state, event, KEYMAP)
|
||||
}
|
||||
|
||||
pub const KEYMAP: &'static [KeyBinding<Sequencer>] = keymap!(Sequencer {
|
||||
[Up, NONE, "cursor_up", "move cursor up", cursor_up],
|
||||
[Down, NONE, "cursor_down", "move cursor down", cursor_down],
|
||||
[Left, NONE, "cursor_left", "move cursor left", cursor_left],
|
||||
[Right, NONE, "cursor_right", "move cursor right", cursor_right],
|
||||
[Char('.'), NONE, "cursor_inc", "increase note duration", cursor_duration_inc],
|
||||
[Char(','), NONE, "cursor_dec", "decrease note duration", cursor_duration_dec],
|
||||
[Char('`'), NONE, "mode_next", "Next view mode", mode_next],
|
||||
[Char('='), NONE, "zoom_in", "Zoom in", zoom_in],
|
||||
[Char('-'), NONE, "zoom_out", "Zoom out", zoom_out],
|
||||
[Char('a'), NONE, "note_add", "Add note", note_add],
|
||||
[Char('z'), NONE, "note_del", "Delete note", note_del],
|
||||
[CapsLock, NONE, "advance", "Toggle auto advance", nop],
|
||||
[Char('w'), NONE, "rest", "Advance by note duration", nop],
|
||||
[Char(' '), NONE, "toggle_play", "Toggle play/pause", toggle_play],
|
||||
[Char('r'), NONE, "toggle_record", "Toggle recording", toggle_record],
|
||||
[Char('d'), NONE, "toggle_overdub", "Toggle overdub", toggle_overdub],
|
||||
[Char('m'), NONE, "toggle_monitor", "Toggle input monitoring", toggle_monitor],
|
||||
[Char('s'), NONE, "stop_and_rewind", "Stop and rewind", stop_and_rewind],
|
||||
[Char('q'), NONE, "quantize_next", "Next quantize value", quantize_next],
|
||||
[Char('Q'), SHIFT, "quantize_prev", "Previous quantize value", quantize_prev],
|
||||
[Char('n'), NONE, "note_axis", "Focus note axis", nop],
|
||||
[Char('t'), NONE, "time_axis", "Focus time axis", nop],
|
||||
[Char('v'), NONE, "variations", "Focus variation selector", nop],
|
||||
[Char('s'), SHIFT, "sync", "Focus sync selector", nop],
|
||||
[Char('1'), NONE, "seq_1", "Phrase 1", focus_seq(0)],
|
||||
[Char('2'), NONE, "seq_2", "Phrase 2", focus_seq(1)],
|
||||
[Char('3'), NONE, "seq_3", "Phrase 3", focus_seq(2)],
|
||||
[Char('4'), NONE, "seq_4", "Phrase 4", focus_seq(3)],
|
||||
[Char('5'), NONE, "seq_5", "Phrase 5", focus_seq(4)],
|
||||
[Char('6'), NONE, "seq_6", "Phrase 6", focus_seq(5)],
|
||||
[Char('7'), NONE, "seq_7", "Phrase 7", focus_seq(6)],
|
||||
[Char('8'), NONE, "seq_8", "Phrase 8", focus_seq(7)],
|
||||
});
|
||||
|
||||
const fn focus_seq (i: usize) -> impl Fn(&mut Sequencer)->Usually<bool> {
|
||||
move |s: &mut Sequencer| {
|
||||
s.sequence = Some(i);
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
fn nop (_: &mut Sequencer) -> Usually<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn note_add (s: &mut Sequencer) -> Usually<bool> {
|
||||
if s.sequence.is_none() {
|
||||
return Ok(false)
|
||||
}
|
||||
let ppq = s.timebase.ppq() as usize;
|
||||
let step = s.time_start + s.time_cursor;
|
||||
let start = step as usize * ppq / s.time_zoom;
|
||||
let end = (step + 1) as usize * ppq / s.time_zoom;
|
||||
let key = u7::from_int_lossy((s.note_cursor + s.note_start) as u8);
|
||||
let note_on = MidiMessage::NoteOn { key, vel: 100.into() };
|
||||
let note_off = MidiMessage::NoteOff { key, vel: 100.into() };
|
||||
let sequence = &mut s.phrases[s.sequence.unwrap()].notes;
|
||||
if sequence.contains_key(&start) {
|
||||
sequence.get_mut(&start).unwrap().push(note_on.clone());
|
||||
} else {
|
||||
sequence.insert(start, vec![note_on]);
|
||||
}
|
||||
if sequence.contains_key(&end) {
|
||||
sequence.get_mut(&end).unwrap().push(note_off.clone());
|
||||
} else {
|
||||
sequence.insert(end, vec![note_off]);
|
||||
};
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn note_del (_: &mut Sequencer) -> Usually<bool> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn time_cursor_inc (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.time_cursor = s.time_cursor + 1;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn time_cursor_dec (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.time_cursor = s.time_cursor.saturating_sub(1);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn note_cursor_inc (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.note_cursor = s.note_cursor + 1;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn note_cursor_dec (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.note_cursor = s.note_cursor.saturating_sub(1);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn cursor_up (s: &mut Sequencer) -> Usually<bool> {
|
||||
match s.view {
|
||||
SequencerMode::Vertical => time_cursor_dec(s),
|
||||
SequencerMode::Horizontal => note_cursor_dec(s),
|
||||
_ => Ok(false)
|
||||
}?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn cursor_down (s: &mut Sequencer) -> Usually<bool> {
|
||||
match s.view {
|
||||
SequencerMode::Vertical => time_cursor_inc(s),
|
||||
SequencerMode::Horizontal => note_cursor_inc(s),
|
||||
_ => Ok(false)
|
||||
}?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn cursor_left (s: &mut Sequencer) -> Usually<bool> {
|
||||
match s.view {
|
||||
SequencerMode::Vertical => note_cursor_dec(s),
|
||||
SequencerMode::Horizontal => time_cursor_dec(s),
|
||||
_ => Ok(false)
|
||||
}?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn cursor_right (s: &mut Sequencer) -> Usually<bool> {
|
||||
match s.view {
|
||||
SequencerMode::Vertical => note_cursor_inc(s),
|
||||
SequencerMode::Horizontal => time_cursor_inc(s),
|
||||
_ => Ok(false)
|
||||
}?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn cursor_duration_inc (_: &mut Sequencer) -> Usually<bool> {
|
||||
//s.cursor.2 = s.cursor.2 + 1
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn cursor_duration_dec (_: &mut Sequencer) -> Usually<bool> {
|
||||
//if s.cursor.2 > 0 { s.cursor.2 = s.cursor.2 - 1 }
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn mode_next (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.view = s.view.next();
|
||||
Ok(true)
|
||||
}
|
||||
impl SequencerMode {
|
||||
fn next (&self) -> Self {
|
||||
match self {
|
||||
Self::Horizontal => Self::Vertical,
|
||||
Self::Vertical => Self::Tiny,
|
||||
Self::Tiny => Self::Horizontal,
|
||||
_ => self.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn stop_and_rewind (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.transport.stop()?;
|
||||
s.transport.locate(0)?;
|
||||
s.playing = TransportState::Stopped;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn toggle_play (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.playing = match s.playing {
|
||||
TransportState::Stopped => {
|
||||
s.transport.start()?;
|
||||
TransportState::Starting
|
||||
},
|
||||
_ => {
|
||||
s.transport.stop()?;
|
||||
s.transport.locate(0)?;
|
||||
TransportState::Stopped
|
||||
},
|
||||
};
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn toggle_record (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.recording = !s.recording;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn toggle_overdub (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.overdub = !s.overdub;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn toggle_monitor (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.monitoring = !s.monitoring;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn quantize_next (s: &mut Sequencer) -> Usually<bool> {
|
||||
if s.time_zoom < 64 {
|
||||
s.time_zoom = s.time_zoom * 2;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn quantize_prev (s: &mut Sequencer) -> Usually<bool> {
|
||||
if s.time_zoom > 1 {
|
||||
s.time_zoom = s.time_zoom / 2;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn zoom_in (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.time_zoom = s.time_zoom / 2;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn zoom_out (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.time_zoom = s.time_zoom * 2;
|
||||
Ok(true)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue