mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +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 (
|
pub fn process (_: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
|
||||||
client: &Client,
|
|
||||||
scope: &ProcessScope
|
|
||||||
) -> Control {
|
|
||||||
Control::Continue
|
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())
|
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(())
|
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 (
|
pub fn process (_: &mut Looper, _: &Client, _: &ProcessScope) -> Control {
|
||||||
state: &mut Looper,
|
|
||||||
client: &Client,
|
|
||||||
scope: &ProcessScope
|
|
||||||
) -> Control {
|
|
||||||
Control::Continue
|
Control::Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render (state: &Looper, buf: &mut Buffer, area: Rect)
|
pub fn render (_: &Looper, _: &mut Buffer, _: Rect) -> Usually<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()))?;
|
|
||||||
Ok(Rect::default())
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,9 @@ impl Mixer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process (
|
pub fn process (
|
||||||
mixer: &mut Mixer,
|
_: &mut Mixer,
|
||||||
client: &Client,
|
_: &Client,
|
||||||
scope: &ProcessScope
|
_: &ProcessScope
|
||||||
) -> Control {
|
) -> Control {
|
||||||
Control::Continue
|
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
|
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 })
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,37 +5,47 @@ type Sequence = std::collections::BTreeMap<u32, Vec<::midly::MidiMessage>>;
|
||||||
|
|
||||||
pub struct Sequencer {
|
pub struct Sequencer {
|
||||||
name: String,
|
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,
|
transport: ::jack::Transport,
|
||||||
input_port: Port<MidiIn>,
|
/// Samples per second
|
||||||
input_connect: Vec<String>,
|
rate: Hz,
|
||||||
output_port: Port<MidiOut>,
|
/// Beats per minute
|
||||||
output_connect: Vec<String>,
|
tempo: Tempo,
|
||||||
/// 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>,
|
|
||||||
/// MIDI resolution (a.k.a. PPQ)
|
/// MIDI resolution (a.k.a. PPQ)
|
||||||
ticks_per_beat: u64,
|
ticks_per_beat: u64,
|
||||||
/// Sequencer resolution, e.g. 16 steps per beat.
|
/// Sequencer resolution, e.g. 16 steps per beat.
|
||||||
steps_per_beat: u64,
|
steps_per_beat: u64,
|
||||||
/// Steps in sequence, e.g. 64 16ths = 4 beat loop.
|
/// Steps in sequence, e.g. 64 16ths = 4 beat loop.
|
||||||
steps: u64,
|
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
|
/// Map: tick -> MIDI events at tick
|
||||||
sequence: Sequence,
|
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 {
|
enum SequencerView {
|
||||||
Tiny,
|
Tiny,
|
||||||
Compact,
|
Compact,
|
||||||
|
|
@ -55,19 +65,20 @@ impl Sequencer {
|
||||||
time_cursor: 0,
|
time_cursor: 0,
|
||||||
rate: Hz(client.sample_rate() as u32),
|
rate: Hz(client.sample_rate() as u32),
|
||||||
tempo: Tempo(120000),
|
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,
|
ticks_per_beat: 96,
|
||||||
steps_per_beat: 8,
|
steps_per_beat: 8,
|
||||||
steps: 64,
|
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)
|
}).activate(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,47 +112,6 @@ impl Sequencer {
|
||||||
#[inline] fn tps (&self) -> f64 {
|
#[inline] fn tps (&self) -> f64 {
|
||||||
self.bps() * self.tpb()
|
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)> {
|
fn frames_to_ticks (&self, start: u64, end: u64) -> Vec<(u64, u64)> {
|
||||||
let fpl = self.fpl() as 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)
|
fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
-> Usually<Rect>
|
|
||||||
{
|
|
||||||
let Rect { x, y, width, .. } = area;
|
let Rect { x, y, width, .. } = area;
|
||||||
let (time0, time1) = s.time_axis;
|
let (time0, time1) = s.time_axis;
|
||||||
let (note0, note1) = s.note_axis;
|
let (note0, note1) = s.note_axis;
|
||||||
|
|
@ -289,7 +257,7 @@ fn render (s: &Sequencer, buf: &mut Buffer, area: Rect)
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
width: header.width.max(piano.width),
|
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 {
|
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 {
|
for key in note0..note1 {
|
||||||
let color = if s.notes_on[key as usize] {
|
let color = if s.notes_on[key as usize] {
|
||||||
Style::default().red()
|
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(
|
buf.set_string(
|
||||||
x + 5 + s.note_cursor,
|
x + 5 + s.note_cursor,
|
||||||
y + s.time_cursor / 2,
|
y + s.time_cursor / 2,
|
||||||
if s.time_cursor % 2 == 0 { "▀" } else { "▄" },
|
if s.time_cursor % 2 == 0 { "▀" } else { "▄" },
|
||||||
Style::default()
|
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 {
|
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(
|
buf.set_string(
|
||||||
x + 5 + s.time_cursor,
|
x + 5 + s.time_cursor,
|
||||||
y + s.note_cursor / 2,
|
y + s.note_cursor / 2,
|
||||||
if s.note_cursor % 2 == 0 { "▀" } else { "▄" },
|
if s.note_cursor % 2 == 0 { "▀" } else { "▄" },
|
||||||
Style::default()
|
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>> {
|
pub fn handle (s: &mut Sequencer, event: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||||
match event {
|
handle_keymap(COMMANDS, s, event)
|
||||||
AppEvent::Input(Event::Key(event)) => {
|
|
||||||
for (code, _, _, command) in COMMANDS.iter() {
|
|
||||||
if *code == event.code {
|
|
||||||
command(s);
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const COMMANDS: [(KeyCode, &'static str, &'static str, &'static dyn Fn(&mut Sequencer));18] = [
|
pub const COMMANDS: &'static [KeyBinding<Sequencer>] = keymap!(Sequencer {
|
||||||
(KeyCode::Up, "cursor_up", "move cursor up", &cursor_up),
|
[Up, NONE, "cursor_up", "move cursor up", cursor_up],
|
||||||
(KeyCode::Down, "cursor_down", "move cursor down", &cursor_down),
|
[Down, NONE, "cursor_down", "move cursor down", cursor_down],
|
||||||
(KeyCode::Left, "cursor_left", "move cursor left", &cursor_left),
|
[Left, NONE, "cursor_left", "move cursor left", cursor_left],
|
||||||
(KeyCode::Right, "cursor_right", "move cursor right", &cursor_right),
|
[Right, NONE, "cursor_right", "move cursor right", cursor_right],
|
||||||
(KeyCode::Char(']'), "cursor_inc", "increase note duration", &cursor_inc),
|
[Char(']'), NONE, "cursor_inc", "increase note duration", cursor_duration_inc],
|
||||||
(KeyCode::Char('['), "cursor_dec", "decrease note duration", &cursor_dec),
|
[Char('['), NONE, "cursor_dec", "decrease note duration", cursor_duration_dec],
|
||||||
(KeyCode::Char('`'), "mode_next", "next view mode", &mode_next),
|
[Char('`'), NONE, "mode_next", "Next view mode", mode_next],
|
||||||
(KeyCode::Tab, "mode_prev", "previous view mode", &mode_prev),
|
[Char('+'), NONE, "zoom_in", "Zoom in", nop],
|
||||||
(KeyCode::Char('+'), "zoom_in", "Zoom in", &nop),
|
[Char('-'), NONE, "zoom_out", "Zoom out", nop],
|
||||||
(KeyCode::Char('-'), "zoom_out", "Zoom out", &nop),
|
[Char('a'), NONE, "note_add", "Add note", note_add],
|
||||||
(KeyCode::Char('a'), "note_add", "Add note", ¬e_add),
|
[Char('d'), NONE, "note_del", "Delete note", note_del],
|
||||||
(KeyCode::Char('d'), "note_del", "Delete note", ¬e_del),
|
[CapsLock, NONE, "advance", "Toggle auto advance", nop],
|
||||||
(KeyCode::CapsLock, "advance", "Toggle auto advance", &nop),
|
[Char('w'), NONE, "rest", "Advance by note duration", nop],
|
||||||
(KeyCode::Char('w'), "rest", "Advance by note duration", &nop),
|
[Char('r'), NONE, "record", "Toggle recodring", toggle_record],
|
||||||
(KeyCode::Char('r'), "record", "Toggle recodring", &toggle_record),
|
[Char('o'), NONE, "overdub", "Toggle overdub", toggle_overdub],
|
||||||
(KeyCode::Char('o'), "overdub", "Toggle overdub", &toggle_overdub),
|
[Char('p'), NONE, "play", "Toggle play/pause", toggle_play],
|
||||||
(KeyCode::Char('p'), "play", "Toggle play/pause", &toggle_play),
|
[Char('s'), NONE, "stop", "Stop and rewind", stop_and_rewind],
|
||||||
(KeyCode::Char('s'), "stop", "Stop and rewind", &nop),
|
[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) {
|
fn nop (_: &mut Sequencer) {
|
||||||
}
|
}
|
||||||
|
|
@ -532,21 +516,33 @@ fn note_add (s: &mut Sequencer) {
|
||||||
}
|
}
|
||||||
fn note_del (_: &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;
|
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;
|
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 {
|
match s.mode {
|
||||||
SequencerView::Vertical => { s.time_cursor = ((time + s.time_cursor) - 1) % time },
|
SequencerView::Vertical => time_cursor_dec(s),
|
||||||
SequencerView::Horizontal => { s.note_cursor = ((note + s.note_cursor) - 1) % note },
|
SequencerView::Horizontal => note_cursor_dec(s),
|
||||||
_ => unimplemented!()
|
_ => unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn cursor_down (s: &mut Sequencer) {
|
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 {
|
match s.mode {
|
||||||
SequencerView::Vertical => { s.time_cursor = ((time + s.time_cursor) + 1) % time },
|
SequencerView::Vertical => time_cursor_inc(s),
|
||||||
SequencerView::Horizontal => { s.note_cursor = ((note + s.note_cursor) + 1) % note },
|
SequencerView::Horizontal => note_cursor_inc(s),
|
||||||
_ => unimplemented!()
|
_ => unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -554,8 +550,8 @@ fn cursor_left (s: &mut Sequencer) {
|
||||||
let time = s.time_axis.1 - s.time_axis.0;
|
let time = s.time_axis.1 - s.time_axis.0;
|
||||||
let note = s.note_axis.1 - s.note_axis.0;
|
let note = s.note_axis.1 - s.note_axis.0;
|
||||||
match s.mode {
|
match s.mode {
|
||||||
SequencerView::Vertical => { s.note_cursor = ((note + s.note_cursor) - 1) % note },
|
SequencerView::Vertical => note_cursor_dec(s),
|
||||||
SequencerView::Horizontal => { s.time_cursor = ((time + s.time_cursor) - 1) % time },
|
SequencerView::Horizontal => time_cursor_dec(s),
|
||||||
_ => unimplemented!()
|
_ => unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -563,22 +559,31 @@ fn cursor_right (s: &mut Sequencer) {
|
||||||
let time = s.time_axis.1 - s.time_axis.0;
|
let time = s.time_axis.1 - s.time_axis.0;
|
||||||
let note = s.note_axis.1 - s.note_axis.0;
|
let note = s.note_axis.1 - s.note_axis.0;
|
||||||
match s.mode {
|
match s.mode {
|
||||||
SequencerView::Vertical => { s.note_cursor = ((note + s.note_cursor) + 1) % note },
|
SequencerView::Vertical => note_cursor_inc(s),
|
||||||
SequencerView::Horizontal => { s.time_cursor = ((time + s.time_cursor) + 1) % time },
|
SequencerView::Horizontal => time_cursor_inc(s),
|
||||||
_ => unimplemented!()
|
_ => unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn cursor_inc (s: &mut Sequencer) {
|
fn cursor_duration_inc (s: &mut Sequencer) {
|
||||||
//s.cursor.2 = s.cursor.2 + 1
|
//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 }
|
//if s.cursor.2 > 0 { s.cursor.2 = s.cursor.2 - 1 }
|
||||||
}
|
}
|
||||||
fn mode_next (s: &mut Sequencer) {
|
fn mode_next (s: &mut Sequencer) {
|
||||||
s.mode = SequencerView::Horizontal
|
s.mode = s.mode.next()
|
||||||
}
|
}
|
||||||
fn mode_prev (s: &mut Sequencer) {
|
impl SequencerView {
|
||||||
s.mode = SequencerView::Vertical
|
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) {
|
fn toggle_play (s: &mut Sequencer) {
|
||||||
s.playing = !s.playing
|
s.playing = !s.playing
|
||||||
|
|
@ -589,6 +594,16 @@ fn toggle_record (s: &mut Sequencer) {
|
||||||
fn toggle_overdub (s: &mut Sequencer) {
|
fn toggle_overdub (s: &mut Sequencer) {
|
||||||
s.overdub = !s.overdub
|
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 {
|
#[cfg(test)] mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,7 @@ impl Device for Rows {
|
||||||
self.focused = true;
|
self.focused = true;
|
||||||
self.items[self.focus].handle(&AppEvent::Blur)?;
|
self.items[self.focus].handle(&AppEvent::Blur)?;
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {}
|
||||||
println!("{event:?}");
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
@ -94,9 +92,42 @@ impl Columns {
|
||||||
|
|
||||||
impl Device for Columns {
|
impl Device for Columns {
|
||||||
fn handle (&mut self, event: &AppEvent) -> Usually<()> {
|
fn handle (&mut self, event: &AppEvent) -> Usually<()> {
|
||||||
if self.focused {
|
if !self.focused {
|
||||||
self.items[self.focus].handle(event)
|
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 {
|
} 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +142,7 @@ impl Device for Columns {
|
||||||
height: area.height
|
height: area.height
|
||||||
})?;
|
})?;
|
||||||
if self.focused && i == self.focus {
|
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;
|
w = w + result.width;
|
||||||
h = h.max(result.height);
|
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 layout;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
use crate::device::{Sequencer, Transport};
|
use crate::device::*;
|
||||||
use crate::layout::Rows;
|
use crate::layout::*;
|
||||||
|
|
||||||
fn main () -> Result<(), Box<dyn Error>> {
|
fn main () -> Result<(), Box<dyn Error>> {
|
||||||
let _cli = cli::Cli::parse();
|
let _cli = cli::Cli::parse();
|
||||||
|
|
@ -33,8 +33,12 @@ fn main () -> Result<(), Box<dyn Error>> {
|
||||||
//])?),
|
//])?),
|
||||||
//])),
|
//])),
|
||||||
//Box::new(Mixer::new("Mixer#000")?),
|
//Box::new(Mixer::new("Mixer#000")?),
|
||||||
Box::new(Sequencer::new("Melody#000")?),
|
|
||||||
Box::new(Transport::new("Transport")?),
|
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")?),
|
//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 BoxedProcessHandler = Box<dyn FnMut(&Client, &ProcessScope)-> Control + Send>;
|
||||||
|
|
||||||
pub type Jack<N> = AsyncClient<N, ClosureProcessHandler<BoxedProcessHandler>>;
|
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