mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
keymap macros
This commit is contained in:
parent
d39cce271f
commit
a50e022ab6
8 changed files with 233 additions and 199 deletions
|
|
@ -12,54 +12,14 @@ impl Launcher {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn process (
|
||||
client: &Client,
|
||||
scope: &ProcessScope
|
||||
) -> Control {
|
||||
pub fn process (_: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
|
||||
pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
pub fn render (_: &Launcher, _: &mut Buffer, _: Rect) -> Usually<Rect> {
|
||||
Ok(Rect::default())
|
||||
}
|
||||
|
||||
pub fn handle (state: &mut Launcher, event: &Event) -> Result<(), Box<dyn Error>> {
|
||||
pub fn handle (_: &mut Launcher, _: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
//let mut x = areas[1].x;
|
||||
//for (index, track) in [
|
||||
//"Track 1",
|
||||
//"Track 2",
|
||||
//"Track 3",
|
||||
//"Track 4",
|
||||
//"Track 5",
|
||||
//"Bus 1",
|
||||
//"Bus 2",
|
||||
//"Mix",
|
||||
//].iter().enumerate() {
|
||||
//buffer.set_string(
|
||||
//x + 10 * (index + 1) as u16, areas[1].y,
|
||||
//"┬", Style::default().not_bold().dim()
|
||||
//);
|
||||
//buffer.set_string(
|
||||
//x + 10 * (index + 1) as u16, areas[1].y + areas[1].height - 1,
|
||||
//"┴", Style::default().not_bold().dim()
|
||||
//);
|
||||
//for y in areas[1].y+1..areas[1].y+areas[1].height - 1 {
|
||||
//buffer.set_string(
|
||||
//x + 10 * (index + 1) as u16, y,
|
||||
//"│", Style::default().not_bold().gray().dim()
|
||||
//);
|
||||
//}
|
||||
//for y in areas[1].y+2..areas[1].y+areas[1].height - 1 {
|
||||
//buffer.set_string(
|
||||
//x + 10 * index as u16 + 1, y,
|
||||
//"--------", Style::default().not_bold().gray().dim()
|
||||
//);
|
||||
//}
|
||||
//buffer.set_string(
|
||||
//x + 10 * index as u16 + 1, areas[1].y + 1,
|
||||
//track, Style::default().bold().not_dim()
|
||||
//);
|
||||
//}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,28 +12,15 @@ impl Looper {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn process (
|
||||
state: &mut Looper,
|
||||
client: &Client,
|
||||
scope: &ProcessScope
|
||||
) -> Control {
|
||||
pub fn process (_: &mut Looper, _: &Client, _: &ProcessScope) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
|
||||
pub fn render (state: &Looper, buf: &mut Buffer, area: Rect)
|
||||
-> Usually<Rect>
|
||||
{
|
||||
//let move_to = |col, row| MoveTo(offset.0 + col, offset.1 + row);
|
||||
//stdout
|
||||
//.queue(move_to(0, 0))?.queue(Print(" Name Input Length Route"))?
|
||||
//.queue(move_to(0, 1))?.queue(PrintStyledContent(" Metronome [ ] ████ Track 1".bold()))?
|
||||
//.queue(move_to(0, 2))?.queue(PrintStyledContent(" Loop 1 [ ] ████ Track 1".bold()))?
|
||||
//.queue(move_to(0, 3))?.queue(PrintStyledContent(" Loop 2 [ ] ████████ Track 2".bold()))?
|
||||
//.queue(move_to(0, 4))?.queue(PrintStyledContent(" Loop 3 [ ] ████████ Track 3".bold()))?;
|
||||
pub fn render (_: &Looper, _: &mut Buffer, _: Rect) -> Usually<Rect> {
|
||||
Ok(Rect::default())
|
||||
}
|
||||
|
||||
pub fn handle (state: &mut Looper, event: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
pub fn handle (_: &mut Looper, _: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ impl Mixer {
|
|||
}
|
||||
|
||||
pub fn process (
|
||||
mixer: &mut Mixer,
|
||||
client: &Client,
|
||||
scope: &ProcessScope
|
||||
_: &mut Mixer,
|
||||
_: &Client,
|
||||
_: &ProcessScope
|
||||
) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ impl Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn process (state: &mut Plugin, client: &Client, scope: &ProcessScope) -> Control {
|
||||
pub fn process (_: &mut Plugin, _: &Client, _: &ProcessScope) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
|
||||
|
|
@ -31,6 +31,6 @@ pub fn render (state: &Plugin, buf: &mut Buffer, Rect { x, y, width, height }: R
|
|||
Ok(Rect { x, y, width: 40, height: 7 })
|
||||
}
|
||||
|
||||
pub fn handle (state: &mut Plugin, event: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
pub fn handle (_: &mut Plugin, _: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,37 +5,47 @@ type Sequence = std::collections::BTreeMap<u32, Vec<::midly::MidiMessage>>;
|
|||
|
||||
pub struct Sequencer {
|
||||
name: String,
|
||||
mode: SequencerView,
|
||||
note_axis: (u16, u16),
|
||||
note_cursor: u16,
|
||||
time_axis: (u16, u16),
|
||||
time_cursor: u16,
|
||||
rate: Hz,
|
||||
tempo: Tempo,
|
||||
transport: ::jack::Transport,
|
||||
input_port: Port<MidiIn>,
|
||||
input_connect: Vec<String>,
|
||||
output_port: Port<MidiOut>,
|
||||
output_connect: Vec<String>,
|
||||
/// Play sequence to output.
|
||||
playing: bool,
|
||||
/// Write input to sequence.
|
||||
recording: bool,
|
||||
/// Don't delete when recording.
|
||||
overdub: bool,
|
||||
/// Red keys on piano roll.
|
||||
notes_on: Vec<bool>,
|
||||
/// Samples per second
|
||||
rate: Hz,
|
||||
/// Beats per minute
|
||||
tempo: Tempo,
|
||||
/// MIDI resolution (a.k.a. PPQ)
|
||||
ticks_per_beat: u64,
|
||||
/// Sequencer resolution, e.g. 16 steps per beat.
|
||||
steps_per_beat: u64,
|
||||
/// Steps in sequence, e.g. 64 16ths = 4 beat loop.
|
||||
steps: u64,
|
||||
input_port: Port<MidiIn>,
|
||||
input_connect: Vec<String>,
|
||||
/// Play input through output.
|
||||
monitoring: bool,
|
||||
/// Red keys on piano roll.
|
||||
notes_on: Vec<bool>,
|
||||
/// Write input to sequence.
|
||||
recording: bool,
|
||||
/// Map: tick -> MIDI events at tick
|
||||
sequence: Sequence,
|
||||
/// Don't delete when recording.
|
||||
overdub: bool,
|
||||
/// Play sequence to output.
|
||||
playing: bool,
|
||||
output_port: Port<MidiOut>,
|
||||
output_connect: Vec<String>,
|
||||
|
||||
/// Display mode
|
||||
mode: SequencerView,
|
||||
/// Range of notes to display
|
||||
note_axis: (u16, u16),
|
||||
/// Position of cursor within note range
|
||||
note_cursor: u16,
|
||||
/// Range of time steps to display
|
||||
time_axis: (u16, u16),
|
||||
/// Position of cursor within time range
|
||||
time_cursor: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
enum SequencerView {
|
||||
Tiny,
|
||||
Compact,
|
||||
|
|
@ -55,19 +65,20 @@ impl Sequencer {
|
|||
time_cursor: 0,
|
||||
rate: Hz(client.sample_rate() as u32),
|
||||
tempo: Tempo(120000),
|
||||
transport: client.transport(),
|
||||
input_port: client.register_port("in", MidiIn::default())?,
|
||||
input_connect: vec!["nanoKEY Studio * (capture): *".into()],
|
||||
output_port: client.register_port("out", MidiOut::default())?,
|
||||
output_connect: vec![],
|
||||
sequence: std::collections::BTreeMap::new(),
|
||||
playing: true,
|
||||
recording: true,
|
||||
overdub: true,
|
||||
notes_on: vec![false;128],
|
||||
ticks_per_beat: 96,
|
||||
steps_per_beat: 8,
|
||||
steps: 64,
|
||||
transport: client.transport(),
|
||||
input_port: client.register_port("in", MidiIn::default())?,
|
||||
input_connect: vec!["nanoKEY Studio * (capture): *".into()],
|
||||
monitoring: true,
|
||||
notes_on: vec![false;128],
|
||||
playing: true,
|
||||
recording: true,
|
||||
sequence: std::collections::BTreeMap::new(),
|
||||
overdub: true,
|
||||
output_port: client.register_port("out", MidiOut::default())?,
|
||||
output_connect: vec![],
|
||||
}).activate(client)
|
||||
}
|
||||
|
||||
|
|
@ -101,47 +112,6 @@ impl Sequencer {
|
|||
#[inline] fn tps (&self) -> f64 {
|
||||
self.bps() * self.tpb()
|
||||
}
|
||||
/// Ticks per loop яснота
|
||||
#[inline] fn tpl (&self) -> f64 {
|
||||
self.tpb() * self.steps as f64 / self.steps_per_beat as f64
|
||||
}
|
||||
|
||||
/// Length of sequence in beat notes and remainder.
|
||||
#[inline] fn beats (&self) -> (u64, u64) {
|
||||
(self.steps / self.steps_per_beat, self.steps % self.steps_per_beat)
|
||||
}
|
||||
/// Length of sequence in ticks, rounded down.
|
||||
#[inline] fn ticks (&self) -> u64 {
|
||||
self.steps * self.ticks_per_beat / self.steps_per_beat
|
||||
}
|
||||
/// Length of sequence in frames.
|
||||
#[inline] fn frames (&self) -> u64 {
|
||||
self.steps * self.usec_per_step().0
|
||||
}
|
||||
/// Ticks per step, rounded down.
|
||||
#[inline] fn ticks_per_step (&self) -> u64 {
|
||||
self.ticks_per_beat / self.steps_per_beat
|
||||
}
|
||||
/// Microseconds per step for current tempo.
|
||||
#[inline] fn usec_per_step (&self) -> Usec {
|
||||
self.tempo.usec_per_step(self.steps_per_beat as u64)
|
||||
}
|
||||
/// Microseconds per tick for current tempo.
|
||||
#[inline] fn usec_per_tick (&self) -> Usec {
|
||||
Usec(self.tempo.usec_per_beat().0 / self.ticks_per_beat)
|
||||
}
|
||||
/// Convert frame to microsecond for current sample rate.
|
||||
#[inline] fn frame_to_usec (&self, frame: u32) -> Usec {
|
||||
Frame(frame).to_usec(&self.rate)
|
||||
}
|
||||
/// Convert frame to tick for current sample rate, tempo, and PPQ.
|
||||
#[inline] fn frame_to_tick (&self, frame: Frames) -> u32 {
|
||||
(self.frame_to_usec(frame).0 / self.usec_per_tick().0) as u32
|
||||
}
|
||||
/// Convert tick to usec for current sample rate, tempo, and PPQ.
|
||||
#[inline] fn tick_to_usec (&self, tick: u32) -> u32 {
|
||||
(tick as u64 * self.usec_per_tick().0) as u32
|
||||
}
|
||||
|
||||
fn frames_to_ticks (&self, start: u64, end: u64) -> Vec<(u64, u64)> {
|
||||
let fpl = self.fpl() as u64;
|
||||
|
|
@ -254,9 +224,7 @@ fn process_out (s: &mut Sequencer, scope: &ProcessScope) {
|
|||
}
|
||||
}
|
||||
|
||||
fn render (s: &Sequencer, buf: &mut Buffer, area: Rect)
|
||||
-> Usually<Rect>
|
||||
{
|
||||
fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let Rect { x, y, width, .. } = area;
|
||||
let (time0, time1) = s.time_axis;
|
||||
let (note0, note1) = s.note_axis;
|
||||
|
|
@ -289,7 +257,7 @@ fn render (s: &Sequencer, buf: &mut Buffer, area: Rect)
|
|||
x,
|
||||
y,
|
||||
width: header.width.max(piano.width),
|
||||
height: header.height + piano.height + 1
|
||||
height: header.height + piano.height + 3
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -398,7 +366,7 @@ fn draw_vertical (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: u64) -> Usu
|
|||
}
|
||||
}
|
||||
if beat % s.steps == step as u64 {
|
||||
buf.set_string(x + 39 - 2, y, if beat % 2 == 0 { "▀" } else { "▄" }, Style::default().yellow());
|
||||
buf.set_string(x + 4, y, if beat % 2 == 0 { "▀" } else { "▄" }, Style::default().yellow());
|
||||
for key in note0..note1 {
|
||||
let color = if s.notes_on[key as usize] {
|
||||
Style::default().red()
|
||||
|
|
@ -408,13 +376,24 @@ fn draw_vertical (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: u64) -> Usu
|
|||
}
|
||||
}
|
||||
}
|
||||
let height = (time1-time0)/2;
|
||||
buf.set_string(x + 2, y + height + 1, format!(
|
||||
"Q 1/{} | N {} ({}-{}) | T {} ({}-{})",
|
||||
4 * s.steps_per_beat,
|
||||
s.note_axis.0 + s.note_cursor,
|
||||
s.note_axis.0,
|
||||
s.note_axis.1 - 1,
|
||||
s.time_axis.0 + s.time_cursor + 1,
|
||||
s.time_axis.0 + 1,
|
||||
s.time_axis.1,
|
||||
), Style::default().dim());
|
||||
buf.set_string(
|
||||
x + 5 + s.note_cursor,
|
||||
y + s.time_cursor / 2,
|
||||
if s.time_cursor % 2 == 0 { "▀" } else { "▄" },
|
||||
Style::default()
|
||||
);
|
||||
Ok(Rect { x, y, width: area.width, height: (time1-time0)/2 })
|
||||
Ok(Rect { x, y, width: area.width, height })
|
||||
}
|
||||
|
||||
fn contains_note_on (sequence: &Sequence, k: ::midly::num::u7, start: u32, end: u32) -> bool {
|
||||
|
|
@ -462,50 +441,55 @@ fn draw_horizontal (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect
|
|||
}
|
||||
}
|
||||
}
|
||||
let height = 32.max(note1 - note0) / 2;
|
||||
buf.set_string(x + 2, y + height + 1, format!(
|
||||
" Q 1/{} | N {} ({}-{}) | T {} ({}-{})",
|
||||
4 * s.steps_per_beat,
|
||||
s.note_axis.0 + s.note_cursor,
|
||||
s.note_axis.0,
|
||||
s.note_axis.1 - 1,
|
||||
s.time_axis.0 + s.time_cursor + 1,
|
||||
s.time_axis.0 + 1,
|
||||
s.time_axis.1,
|
||||
), Style::default().dim());
|
||||
buf.set_string(
|
||||
x + 5 + s.time_cursor,
|
||||
y + s.note_cursor / 2,
|
||||
if s.note_cursor % 2 == 0 { "▀" } else { "▄" },
|
||||
Style::default()
|
||||
);
|
||||
Ok(Rect { x, y, width: time1 - time0 + 6, height: 32.max(note1 - note0) / 2 })
|
||||
Ok(Rect { x, y, width: time1 - time0 + 6, height})
|
||||
}
|
||||
|
||||
pub fn handle (s: &mut Sequencer, event: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
match event {
|
||||
AppEvent::Input(Event::Key(event)) => {
|
||||
for (code, _, _, command) in COMMANDS.iter() {
|
||||
if *code == event.code {
|
||||
command(s);
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
};
|
||||
Ok(())
|
||||
handle_keymap(COMMANDS, s, event)
|
||||
}
|
||||
|
||||
const COMMANDS: [(KeyCode, &'static str, &'static str, &'static dyn Fn(&mut Sequencer));18] = [
|
||||
(KeyCode::Up, "cursor_up", "move cursor up", &cursor_up),
|
||||
(KeyCode::Down, "cursor_down", "move cursor down", &cursor_down),
|
||||
(KeyCode::Left, "cursor_left", "move cursor left", &cursor_left),
|
||||
(KeyCode::Right, "cursor_right", "move cursor right", &cursor_right),
|
||||
(KeyCode::Char(']'), "cursor_inc", "increase note duration", &cursor_inc),
|
||||
(KeyCode::Char('['), "cursor_dec", "decrease note duration", &cursor_dec),
|
||||
(KeyCode::Char('`'), "mode_next", "next view mode", &mode_next),
|
||||
(KeyCode::Tab, "mode_prev", "previous view mode", &mode_prev),
|
||||
(KeyCode::Char('+'), "zoom_in", "Zoom in", &nop),
|
||||
(KeyCode::Char('-'), "zoom_out", "Zoom out", &nop),
|
||||
(KeyCode::Char('a'), "note_add", "Add note", ¬e_add),
|
||||
(KeyCode::Char('d'), "note_del", "Delete note", ¬e_del),
|
||||
(KeyCode::CapsLock, "advance", "Toggle auto advance", &nop),
|
||||
(KeyCode::Char('w'), "rest", "Advance by note duration", &nop),
|
||||
(KeyCode::Char('r'), "record", "Toggle recodring", &toggle_record),
|
||||
(KeyCode::Char('o'), "overdub", "Toggle overdub", &toggle_overdub),
|
||||
(KeyCode::Char('p'), "play", "Toggle play/pause", &toggle_play),
|
||||
(KeyCode::Char('s'), "stop", "Stop and rewind", &nop),
|
||||
];
|
||||
pub const COMMANDS: &'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", nop],
|
||||
[Char('-'), NONE, "zoom_out", "Zoom out", nop],
|
||||
[Char('a'), NONE, "note_add", "Add note", note_add],
|
||||
[Char('d'), NONE, "note_del", "Delete note", note_del],
|
||||
[CapsLock, NONE, "advance", "Toggle auto advance", nop],
|
||||
[Char('w'), NONE, "rest", "Advance by note duration", nop],
|
||||
[Char('r'), NONE, "record", "Toggle recodring", toggle_record],
|
||||
[Char('o'), NONE, "overdub", "Toggle overdub", toggle_overdub],
|
||||
[Char('p'), NONE, "play", "Toggle play/pause", toggle_play],
|
||||
[Char('s'), NONE, "stop", "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]
|
||||
});
|
||||
|
||||
fn nop (_: &mut Sequencer) {
|
||||
}
|
||||
|
|
@ -532,21 +516,33 @@ fn note_add (s: &mut Sequencer) {
|
|||
}
|
||||
fn note_del (_: &mut Sequencer) {
|
||||
}
|
||||
fn cursor_up (s: &mut Sequencer) {
|
||||
fn time_cursor_inc (s: &mut Sequencer) {
|
||||
let time = s.time_axis.1 - s.time_axis.0;
|
||||
s.time_cursor = ((time + s.time_cursor) + 1) % time
|
||||
}
|
||||
fn time_cursor_dec (s: &mut Sequencer) {
|
||||
let time = s.time_axis.1 - s.time_axis.0;
|
||||
s.time_cursor = ((time + s.time_cursor) - 1) % time
|
||||
}
|
||||
fn note_cursor_inc (s: &mut Sequencer) {
|
||||
let note = s.note_axis.1 - s.note_axis.0;
|
||||
s.note_cursor = ((note + s.note_cursor) + 1) % note
|
||||
}
|
||||
fn note_cursor_dec (s: &mut Sequencer) {
|
||||
let note = s.note_axis.1 - s.note_axis.0;
|
||||
s.note_cursor = ((note + s.note_cursor) - 1) % note
|
||||
}
|
||||
fn cursor_up (s: &mut Sequencer) {
|
||||
match s.mode {
|
||||
SequencerView::Vertical => { s.time_cursor = ((time + s.time_cursor) - 1) % time },
|
||||
SequencerView::Horizontal => { s.note_cursor = ((note + s.note_cursor) - 1) % note },
|
||||
SequencerView::Vertical => time_cursor_dec(s),
|
||||
SequencerView::Horizontal => note_cursor_dec(s),
|
||||
_ => unimplemented!()
|
||||
}
|
||||
}
|
||||
fn cursor_down (s: &mut Sequencer) {
|
||||
let time = s.time_axis.1 - s.time_axis.0;
|
||||
let note = s.note_axis.1 - s.note_axis.0;
|
||||
match s.mode {
|
||||
SequencerView::Vertical => { s.time_cursor = ((time + s.time_cursor) + 1) % time },
|
||||
SequencerView::Horizontal => { s.note_cursor = ((note + s.note_cursor) + 1) % note },
|
||||
SequencerView::Vertical => time_cursor_inc(s),
|
||||
SequencerView::Horizontal => note_cursor_inc(s),
|
||||
_ => unimplemented!()
|
||||
}
|
||||
}
|
||||
|
|
@ -554,8 +550,8 @@ fn cursor_left (s: &mut Sequencer) {
|
|||
let time = s.time_axis.1 - s.time_axis.0;
|
||||
let note = s.note_axis.1 - s.note_axis.0;
|
||||
match s.mode {
|
||||
SequencerView::Vertical => { s.note_cursor = ((note + s.note_cursor) - 1) % note },
|
||||
SequencerView::Horizontal => { s.time_cursor = ((time + s.time_cursor) - 1) % time },
|
||||
SequencerView::Vertical => note_cursor_dec(s),
|
||||
SequencerView::Horizontal => time_cursor_dec(s),
|
||||
_ => unimplemented!()
|
||||
}
|
||||
}
|
||||
|
|
@ -563,22 +559,31 @@ fn cursor_right (s: &mut Sequencer) {
|
|||
let time = s.time_axis.1 - s.time_axis.0;
|
||||
let note = s.note_axis.1 - s.note_axis.0;
|
||||
match s.mode {
|
||||
SequencerView::Vertical => { s.note_cursor = ((note + s.note_cursor) + 1) % note },
|
||||
SequencerView::Horizontal => { s.time_cursor = ((time + s.time_cursor) + 1) % time },
|
||||
SequencerView::Vertical => note_cursor_inc(s),
|
||||
SequencerView::Horizontal => time_cursor_inc(s),
|
||||
_ => unimplemented!()
|
||||
}
|
||||
}
|
||||
fn cursor_inc (s: &mut Sequencer) {
|
||||
fn cursor_duration_inc (s: &mut Sequencer) {
|
||||
//s.cursor.2 = s.cursor.2 + 1
|
||||
}
|
||||
fn cursor_dec (s: &mut Sequencer) {
|
||||
fn cursor_duration_dec (s: &mut Sequencer) {
|
||||
//if s.cursor.2 > 0 { s.cursor.2 = s.cursor.2 - 1 }
|
||||
}
|
||||
fn mode_next (s: &mut Sequencer) {
|
||||
s.mode = SequencerView::Horizontal
|
||||
s.mode = s.mode.next()
|
||||
}
|
||||
fn mode_prev (s: &mut Sequencer) {
|
||||
s.mode = SequencerView::Vertical
|
||||
impl SequencerView {
|
||||
fn next (&self) -> Self {
|
||||
match self {
|
||||
Self::Horizontal => Self::Vertical,
|
||||
Self::Vertical => Self::Horizontal,
|
||||
_ => self.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn stop_and_rewind (s: &mut Sequencer) {
|
||||
s.playing = false
|
||||
}
|
||||
fn toggle_play (s: &mut Sequencer) {
|
||||
s.playing = !s.playing
|
||||
|
|
@ -589,6 +594,16 @@ fn toggle_record (s: &mut Sequencer) {
|
|||
fn toggle_overdub (s: &mut Sequencer) {
|
||||
s.overdub = !s.overdub
|
||||
}
|
||||
fn quantize_next (s: &mut Sequencer) {
|
||||
if s.steps_per_beat < 64 {
|
||||
s.steps_per_beat = s.steps_per_beat * 2
|
||||
}
|
||||
}
|
||||
fn quantize_prev (s: &mut Sequencer) {
|
||||
if s.steps_per_beat > 1 {
|
||||
s.steps_per_beat = s.steps_per_beat / 2
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)] mod test {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -46,9 +46,7 @@ impl Device for Rows {
|
|||
self.focused = true;
|
||||
self.items[self.focus].handle(&AppEvent::Blur)?;
|
||||
},
|
||||
_ => {
|
||||
println!("{event:?}");
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -94,9 +92,42 @@ impl Columns {
|
|||
|
||||
impl Device for Columns {
|
||||
fn handle (&mut self, event: &AppEvent) -> Usually<()> {
|
||||
if self.focused {
|
||||
self.items[self.focus].handle(event)
|
||||
if !self.focused {
|
||||
match event {
|
||||
AppEvent::Input(Event::Key(KeyEvent { code: KeyCode::Esc, .. })) => {
|
||||
self.focused = true;
|
||||
self.items[self.focus].handle(&AppEvent::Blur)?;
|
||||
Ok(())
|
||||
},
|
||||
_ => self.items[self.focus].handle(event)
|
||||
}
|
||||
} else {
|
||||
match event {
|
||||
AppEvent::Input(event) => match event {
|
||||
Event::Key(KeyEvent { code: KeyCode::Left, .. }) => {
|
||||
if self.focus == 0 {
|
||||
self.focus = self.items.len();
|
||||
}
|
||||
self.focus = self.focus - 1;
|
||||
},
|
||||
Event::Key(KeyEvent { code: KeyCode::Right, .. }) => {
|
||||
self.focus = self.focus + 1;
|
||||
if self.focus >= self.items.len() {
|
||||
self.focus = 0;
|
||||
}
|
||||
},
|
||||
Event::Key(KeyEvent { code: KeyCode::Enter, .. }) => {
|
||||
self.focused = false;
|
||||
self.items[self.focus].handle(&AppEvent::Focus)?;
|
||||
},
|
||||
Event::Key(KeyEvent { code: KeyCode::Esc, .. }) => {
|
||||
self.focused = true;
|
||||
self.items[self.focus].handle(&AppEvent::Blur)?;
|
||||
},
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -111,7 +142,7 @@ impl Device for Columns {
|
|||
height: area.height
|
||||
})?;
|
||||
if self.focused && i == self.focus {
|
||||
draw_box_styled(buf, result, Some(Style::default().yellow()));
|
||||
draw_box_styled(buf, result, Some(Style::default().green()));
|
||||
}
|
||||
w = w + result.width;
|
||||
h = h.max(result.height);
|
||||
|
|
|
|||
10
src/main.rs
10
src/main.rs
|
|
@ -13,8 +13,8 @@ pub mod config;
|
|||
pub mod layout;
|
||||
pub mod time;
|
||||
|
||||
use crate::device::{Sequencer, Transport};
|
||||
use crate::layout::Rows;
|
||||
use crate::device::*;
|
||||
use crate::layout::*;
|
||||
|
||||
fn main () -> Result<(), Box<dyn Error>> {
|
||||
let _cli = cli::Cli::parse();
|
||||
|
|
@ -33,8 +33,12 @@ fn main () -> Result<(), Box<dyn Error>> {
|
|||
//])?),
|
||||
//])),
|
||||
//Box::new(Mixer::new("Mixer#000")?),
|
||||
Box::new(Sequencer::new("Melody#000")?),
|
||||
Box::new(Transport::new("Transport")?),
|
||||
Box::new(Columns::new(true, vec![
|
||||
Box::new(Sequencer::new("Melody#000")?),
|
||||
Box::new(Sequencer::new("Melody#001")?),
|
||||
Box::new(Sequencer::new("Rhythm#000")?),
|
||||
])),
|
||||
//Box::new(Sequencer::new("Rhythm#000")?),
|
||||
]))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,3 +85,40 @@ pub type BoxedNotificationHandler = Box<dyn Fn(AppEvent) + Send>;
|
|||
pub type BoxedProcessHandler = Box<dyn FnMut(&Client, &ProcessScope)-> Control + Send>;
|
||||
|
||||
pub type Jack<N> = AsyncClient<N, ClosureProcessHandler<BoxedProcessHandler>>;
|
||||
|
||||
pub type KeyBinding<T> = (
|
||||
KeyCode, KeyModifiers, &'static str, &'static str, &'static dyn Fn(&mut T)
|
||||
);
|
||||
|
||||
#[macro_export] macro_rules! key {
|
||||
($k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: ident) => {
|
||||
(KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! keymap {
|
||||
($T:ty { $([$k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: ident]),* }) => {
|
||||
&[
|
||||
$((KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as &'static dyn Fn(&mut $T))),*
|
||||
] as &'static [KeyBinding<$T>]
|
||||
}
|
||||
}
|
||||
|
||||
pub use crate::{key, keymap};
|
||||
|
||||
pub fn handle_keymap <T> (
|
||||
commands: &[KeyBinding<T>], state: &mut T, event: &AppEvent
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
match event {
|
||||
AppEvent::Input(Event::Key(event)) => {
|
||||
for (code, modifiers, _, _, command) in commands.iter() {
|
||||
if *code == event.code && modifiers.bits() == event.modifiers.bits() {
|
||||
command(state);
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue