mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
key lights up in response to recording
This commit is contained in:
parent
2118615aea
commit
674f95fcd2
2 changed files with 254 additions and 396 deletions
|
|
@ -12,9 +12,11 @@ pub struct Sequencer {
|
|||
time_cursor: u16,
|
||||
rate: Hz,
|
||||
tempo: Tempo,
|
||||
steps: u64,
|
||||
divisions: u64,
|
||||
transport: ::jack::Transport,
|
||||
sequence: Sequence,
|
||||
ppq: u32,
|
||||
ppq: u64,
|
||||
input_port: Port<MidiIn>,
|
||||
input_connect: Vec<String>,
|
||||
output_port: Port<MidiOut>,
|
||||
|
|
@ -55,6 +57,8 @@ impl Sequencer {
|
|||
recording: true,
|
||||
overdub: true,
|
||||
notes_on: vec![false;128],
|
||||
steps: 64,
|
||||
divisions: 16,
|
||||
}).activate(client)
|
||||
}
|
||||
}
|
||||
|
|
@ -66,32 +70,29 @@ pub fn process (state: &mut Sequencer, client: &Client, scope: &ProcessScope) ->
|
|||
}
|
||||
|
||||
fn process_in (state: &mut Sequencer, scope: &ProcessScope) {
|
||||
let pos = state.transport.query().unwrap().pos;
|
||||
let frame = pos.frame();
|
||||
let rate = pos.frame_rate().unwrap();
|
||||
let usecs = Frame(frame).to_usec(&Hz(rate)).0 as u64;
|
||||
let usec_per_beat = state.tempo.usec_per_beat().0 as u64;
|
||||
let usec_per_tick = state.tempo.usec_per_tick(state.ppq).0 as u64;
|
||||
let beats = usecs / usec_per_beat;
|
||||
let time = beats as u32 * state.ppq;
|
||||
|
||||
let pos = state.transport.query().unwrap().pos;
|
||||
let usecs = Frame(pos.frame()).to_usec(&state.rate).0;
|
||||
let beat = usecs / state.tempo.usec_per_beat().0;
|
||||
let step = usecs / state.tempo.usec_per_step(state.divisions as u64).0;
|
||||
let tick_usec = state.tempo.usec_per_step(state.ppq).0;
|
||||
let tick = usecs / tick_usec;
|
||||
for event in state.input_port.iter(scope) {
|
||||
match midly::live::LiveEvent::parse(event.bytes).unwrap() {
|
||||
midly::live::LiveEvent::Midi { channel, message } => match message {
|
||||
midly::MidiMessage::NoteOn { key, vel } => {
|
||||
state.notes_on[key.as_int() as usize] = true;
|
||||
if state.sequence.contains_key(&time) {
|
||||
state.sequence.get_mut(&time).unwrap().push(message.clone());
|
||||
if state.sequence.contains_key(&(tick as u32)) {
|
||||
state.sequence.get_mut(&(tick as u32)).unwrap().push(message.clone());
|
||||
} else {
|
||||
state.sequence.insert(time, vec![message.clone()]);
|
||||
state.sequence.insert(tick as u32, vec![message.clone()]);
|
||||
}
|
||||
},
|
||||
midly::MidiMessage::NoteOff { key, vel } => {
|
||||
state.notes_on[key.as_int() as usize] = false;
|
||||
if state.sequence.contains_key(&time) {
|
||||
state.sequence.get_mut(&time).unwrap().push(message.clone());
|
||||
if state.sequence.contains_key(&(tick as u32)) {
|
||||
state.sequence.get_mut(&(tick as u32)).unwrap().push(message.clone());
|
||||
} else {
|
||||
state.sequence.insert(time, vec![message.clone()]);
|
||||
state.sequence.insert(tick as u32, vec![message.clone()]);
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
|
|
@ -107,8 +108,15 @@ fn process_out (state: &mut Sequencer, scope: &ProcessScope) {
|
|||
let start = scope.last_frame_time();
|
||||
let end = start + size;
|
||||
let mut writer = state.output_port.writer(scope);
|
||||
let pulse_usec = state.tempo.usec_per_step(state.ppq as u64).0;
|
||||
let tick_start = Frame(start).to_usec(&state.rate).0;
|
||||
let tick_start = tick_start / pulse_usec;
|
||||
let tick_end = Frame(start).to_usec(&state.rate).0;
|
||||
let tick_end = tick_start / pulse_usec;
|
||||
for time in 0..size {
|
||||
// TODO
|
||||
let usecs = Frame(start + time).to_usec(&state.rate).0;
|
||||
let ticks = usecs / state.tempo.usec_per_step(state.ppq as u64).0;
|
||||
//println!("{usecs} = {ticks}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -134,6 +142,226 @@ impl NotificationHandler for Sequencer {
|
|||
}
|
||||
}
|
||||
|
||||
fn render (state: &Sequencer, buf: &mut Buffer, mut area: Rect)
|
||||
-> Usually<Rect>
|
||||
{
|
||||
let Rect { x, y, width, height } = area;
|
||||
let (time0, time1) = state.time_axis;
|
||||
let (note0, note1) = state.note_axis;
|
||||
let pos = state.transport.query().unwrap().pos;
|
||||
let frame = pos.frame();
|
||||
let rate = pos.frame_rate().unwrap();
|
||||
let usecs = Frame(frame).to_usec(&Hz(rate)).0;
|
||||
let usec_per_beat = state.tempo.usec_per_beat().0;
|
||||
let usec_per_tick = state.tempo.usec_per_step(state.ppq).0;
|
||||
let usec_per_step = state.tempo.usec_per_step(state.divisions as u64).0;
|
||||
let steps = usecs / usec_per_step;
|
||||
let header = draw_state_header(state, buf, area, steps)?;
|
||||
let piano = match state.mode {
|
||||
SequencerView::Tiny =>
|
||||
Rect { x, y, width, height: 0 },
|
||||
SequencerView::Compact =>
|
||||
Rect { x, y, width, height: 0 },
|
||||
SequencerView::Vertical => draw_state_vertical(state, buf, Rect {
|
||||
x,
|
||||
y: y + header.height,
|
||||
width: 3 + note1 - note0,
|
||||
height: 3 + time1 - time0,
|
||||
}, steps)?,
|
||||
SequencerView::Horizontal => draw_state_horizontal(state, buf, Rect {
|
||||
x,
|
||||
y: y + header.height,
|
||||
width: 3 + time1 - time0,
|
||||
height: 3 + note1 - note0,
|
||||
})?,
|
||||
};
|
||||
Ok(draw_box(buf, Rect {
|
||||
x,
|
||||
y,
|
||||
width: header.width.max(piano.width),
|
||||
height: header.height + piano.height + 1
|
||||
}))
|
||||
}
|
||||
|
||||
fn draw_state_header (state: &Sequencer, buf: &mut Buffer, area: Rect, beat: u64) -> Usually<Rect> {
|
||||
let rep = beat / state.steps;
|
||||
let step = beat % state.steps;
|
||||
let reps = state.steps / state.divisions;
|
||||
let steps = state.steps % state.divisions;
|
||||
let Rect { x, y, width, height } = area;
|
||||
let style = Style::default().gray();
|
||||
buf.set_string(x + 1, y + 1, &format!(" │ {rep}.{step:2} / {reps}.{steps}"), style);
|
||||
buf.set_string(x + 2, y + 1, &format!("{}", &state.name), style.white().bold());
|
||||
buf.set_string(x + 1, y + 2, &format!(" ▶ PLAY │ ⏹ STOP │ │"), style);
|
||||
buf.set_string(x + 2, y + 2, &format!("▶ PLAY"), if state.playing {
|
||||
Style::default().green()
|
||||
} else {
|
||||
Style::default().dim()
|
||||
});
|
||||
buf.set_string(x + 24, y + 2, &format!("⏺ REC"), if state.recording {
|
||||
Style::default().red()
|
||||
} else {
|
||||
Style::default().dim()
|
||||
});
|
||||
buf.set_string(x + 32, y + 2, &format!("⏺ DUB"), if state.overdub {
|
||||
Style::default().yellow()
|
||||
} else {
|
||||
Style::default().dim()
|
||||
});
|
||||
Ok(Rect { x, y, width: 39, height: 4 })
|
||||
}
|
||||
|
||||
const KEY_WHITE: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: ::ratatui::style::Modifier::empty(),
|
||||
sub_modifier: ::ratatui::style::Modifier::empty(),
|
||||
};
|
||||
|
||||
const KEY_BLACK: Style = Style {
|
||||
fg: Some(Color::Black),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: ::ratatui::style::Modifier::empty(),
|
||||
sub_modifier: ::ratatui::style::Modifier::empty(),
|
||||
};
|
||||
|
||||
const KEY_HORIZONTAL_STYLE: [Style;12] = [
|
||||
KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
|
||||
KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
|
||||
];
|
||||
|
||||
fn draw_state_vertical (state: &Sequencer, buf: &mut Buffer, area: Rect, beat: u64) -> Usually<Rect> {
|
||||
let Rect { x, y, .. } = area;
|
||||
let transport = state.transport.query().unwrap();
|
||||
let (time0, time1) = state.time_axis;
|
||||
let (note0, note1) = state.note_axis;
|
||||
let bw = Style::default().dim();
|
||||
let bg = Style::default().on_black();
|
||||
for key in note0..note1.max(note0+32) {
|
||||
let x = x + 5 + key - note0;
|
||||
if key % 12 == 0 {
|
||||
let octave = format!("C{}", (key / 12) as i8 - 4);
|
||||
buf.set_string(x, y, &octave, Style::default());
|
||||
}
|
||||
let mut color = KEY_HORIZONTAL_STYLE[key as usize % 12];
|
||||
let mut is_on = state.notes_on[key as usize];
|
||||
let step = beat % state.steps;
|
||||
let (a, b, c) = (
|
||||
(step + 0) as u32 * state.ppq as u32 / state.divisions as u32,
|
||||
(step + 1) as u32 * state.ppq as u32 / state.divisions as u32,
|
||||
(step + 2) as u32 * state.ppq as u32 / state.divisions as u32,
|
||||
);
|
||||
let key = ::midly::num::u7::from(key as u8);
|
||||
is_on = is_on || contains_note_on(&state.sequence, key, a, b);
|
||||
is_on = is_on || contains_note_on(&state.sequence, key, b, c);
|
||||
if is_on {
|
||||
color = Style::default().red();
|
||||
}
|
||||
buf.set_string(x, y - 1, &format!("▄"), color);
|
||||
}
|
||||
for step in time0..time1 {
|
||||
let y = y - time0 + step / 2;
|
||||
let step = step as u64;
|
||||
//buf.set_string(x + 5, y, &" ".repeat(32.max(note1-note0)as usize), bg);
|
||||
if step % state.divisions == 0 {
|
||||
buf.set_string(x + 2, y, &format!("{:2} ", step + 1), Style::default());
|
||||
}
|
||||
for k in note0..note1.max(note0+32) {
|
||||
let key = ::midly::num::u7::from_int_lossy(k as u8);
|
||||
if step % 2 == 0 {
|
||||
let (a, b, c) = (
|
||||
(step + 0) as u32 * state.ppq as u32 / state.divisions as u32,
|
||||
(step + 1) as u32 * state.ppq as u32 / state.divisions as u32,
|
||||
(step + 2) as u32 * state.ppq as u32 / state.divisions as u32,
|
||||
);
|
||||
let (character, style) = match (
|
||||
contains_note_on(&state.sequence, key, a, b),
|
||||
contains_note_on(&state.sequence, key, b, c),
|
||||
) {
|
||||
(true, true) => ("█", bg),
|
||||
(true, false) => ("▀", bg),
|
||||
(false, true) => ("▄", bg),
|
||||
(false, false) => (" ", bg),
|
||||
};
|
||||
buf.set_string(x + 5 + k - note0, y, character, style);
|
||||
}
|
||||
}
|
||||
if beat % state.steps == step as u64 {
|
||||
buf.set_string(x + 1, y, if beat % 2 == 0 { "▀" } else { "▄" }, Style::default().yellow());
|
||||
for key in note0..note1.max(note0+32) {
|
||||
let color = if state.notes_on[key as usize] {
|
||||
Style::default().red()
|
||||
} else {
|
||||
KEY_HORIZONTAL_STYLE[key as usize % 12]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.set_string(
|
||||
x + 5 + state.note_cursor,
|
||||
y + state.time_cursor / 2,
|
||||
if state.time_cursor % 2 == 0 { "▀" } else { "▄" },
|
||||
Style::default()
|
||||
);
|
||||
Ok(Rect { x, y, width: area.width, height: (time1-time0)/2 })
|
||||
}
|
||||
|
||||
fn contains_note_on (sequence: &Sequence, k: ::midly::num::u7, start: u32, end: u32) -> bool {
|
||||
for (i, (t, events)) in sequence.range(start..end).enumerate() {
|
||||
for event in events.iter() {
|
||||
match event {
|
||||
::midly::MidiMessage::NoteOn {key,..} => {
|
||||
if *key == k {
|
||||
return true
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const KEYS_VERTICAL: [&'static str; 6] = [
|
||||
"▀", "▀", "▀", "█", "▄", "▄",
|
||||
];
|
||||
|
||||
fn draw_state_horizontal (state: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let Rect { x, y, .. } = area;
|
||||
let (time0, time1) = state.time_axis;
|
||||
let (note0, note1) = state.note_axis;
|
||||
let bw = Style::default().dim();
|
||||
let bg = Style::default().on_black();
|
||||
for i in 0..32.max(note1-note0)/2 {
|
||||
let y = y + i;
|
||||
buf.set_string(x + 1, y, KEYS_VERTICAL[(i % 6) as usize], bw);
|
||||
buf.set_string(x + 2, y, "█", bw);
|
||||
buf.set_string(x + 5, y, &" ".repeat((time1 - time0) as usize), bg);
|
||||
if i % 6 == 0 {
|
||||
let octave = format!("C{}", ((note1 - i) / 6) as i8 - 4);
|
||||
buf.set_string(x+5, y, &octave, Style::default());
|
||||
}
|
||||
}
|
||||
for step in time0..time1 {
|
||||
let time_start = step as u32 * state.ppq as u32;
|
||||
let time_end = (step + 1) as u32 * state.ppq as u32;
|
||||
for (i, (t, events)) in state.sequence.range(time_start..time_end).enumerate() {
|
||||
if events.len() > 0 {
|
||||
buf.set_string(x + 5 + step as u16, y, "█", bw);
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.set_string(
|
||||
x + 5 + state.time_cursor,
|
||||
y + state.note_cursor / 2,
|
||||
if state.note_cursor % 2 == 0 { "▀" } else { "▄" },
|
||||
Style::default()
|
||||
);
|
||||
Ok(Rect { x, y, width: time1 - time0 + 6, height: 32.max(note1 - note0) / 2 })
|
||||
}
|
||||
|
||||
pub fn handle (state: &mut Sequencer, event: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
match event {
|
||||
AppEvent::Input(Event::Key(event)) => {
|
||||
|
|
@ -174,8 +402,8 @@ fn nop (_: &mut Sequencer) {
|
|||
}
|
||||
fn note_add (s: &mut Sequencer) {
|
||||
let time = (s.time_axis.0 + s.time_cursor) as u32;
|
||||
let time_start = time * s.ppq;
|
||||
let time_end = (time + 1) * s.ppq;
|
||||
let time_start = time * s.ppq as u32;
|
||||
let time_end = (time + 1) * s.ppq as u32;
|
||||
let key = ::midly::num::u7::from_int_lossy((s.note_cursor + s.note_axis.0) as u8);
|
||||
let note_on = ::midly::MidiMessage::NoteOn { key, vel: 100.into() };
|
||||
let note_off = ::midly::MidiMessage::NoteOff { key, vel: 100.into() };
|
||||
|
|
@ -249,373 +477,3 @@ fn toggle_record (s: &mut Sequencer) {
|
|||
fn toggle_overdub (s: &mut Sequencer) {
|
||||
s.overdub = !s.overdub
|
||||
}
|
||||
|
||||
fn render (sequencer: &Sequencer, buf: &mut Buffer, mut area: Rect)
|
||||
-> Usually<Rect>
|
||||
{
|
||||
let Rect { x, y, width, height } = area;
|
||||
let (time0, time1) = sequencer.time_axis;
|
||||
let (note0, note1) = sequencer.note_axis;
|
||||
let pos = sequencer.transport.query().unwrap().pos;
|
||||
let frame = pos.frame();
|
||||
let rate = pos.frame_rate().unwrap();
|
||||
let usecs = Frame(frame).to_usec(&Hz(rate)).0 as u64;
|
||||
let usec_per_beat = sequencer.tempo.usec_per_beat().0 as u64;
|
||||
let usec_per_tick = sequencer.tempo.usec_per_tick(sequencer.ppq).0 as u64;
|
||||
let beats = usecs / usec_per_beat;
|
||||
let bar = beats / 4;
|
||||
let beat = beats % 4;
|
||||
let tick = (usecs % usec_per_beat) / usec_per_tick;
|
||||
let header = draw_sequencer_header(sequencer, buf, area, bar, beat, tick)?;
|
||||
let piano = match sequencer.mode {
|
||||
SequencerView::Tiny =>
|
||||
Rect { x, y, width, height: 0 },
|
||||
SequencerView::Compact =>
|
||||
Rect { x, y, width, height: 0 },
|
||||
SequencerView::Vertical => draw_sequencer_vertical(sequencer, buf, Rect {
|
||||
x,
|
||||
y: y + header.height,
|
||||
width: 3 + note1 - note0,
|
||||
height: 3 + time1 - time0,
|
||||
}, beats)?,
|
||||
SequencerView::Horizontal => draw_sequencer_horizontal(sequencer, buf, Rect {
|
||||
x,
|
||||
y: y + header.height,
|
||||
width: 3 + time1 - time0,
|
||||
height: 3 + note1 - note0,
|
||||
})?,
|
||||
};
|
||||
Ok(draw_box(buf, Rect {
|
||||
x,
|
||||
y,
|
||||
width: header.width.max(piano.width),
|
||||
height: header.height + piano.height + 1
|
||||
}))
|
||||
}
|
||||
|
||||
fn draw_sequencer_header (sequencer: &Sequencer, buf: &mut Buffer, area: Rect, bar: u64, beat: u64, tick: u64) -> Usually<Rect> {
|
||||
let Rect { x, y, width, height } = area;
|
||||
let style = Style::default().gray();
|
||||
buf.set_string(x + 1, y + 1, &format!(" │ {bar}.{beat}.{tick:03}"), style);
|
||||
buf.set_string(x + 2, y + 1, &format!("{}", &sequencer.name), style.white().bold());
|
||||
buf.set_string(x + 1, y + 2, &format!(" ▶ PLAY │ ⏹ STOP │ │"), style);
|
||||
buf.set_string(x + 2, y + 2, &format!("▶ PLAY"), if sequencer.playing {
|
||||
Style::default().green()
|
||||
} else {
|
||||
Style::default().dim()
|
||||
});
|
||||
buf.set_string(x + 24, y + 2, &format!("⏺ REC"), if sequencer.recording {
|
||||
Style::default().red()
|
||||
} else {
|
||||
Style::default().dim()
|
||||
});
|
||||
buf.set_string(x + 32, y + 2, &format!("⏺ DUB"), if sequencer.overdub {
|
||||
Style::default().yellow()
|
||||
} else {
|
||||
Style::default().dim()
|
||||
});
|
||||
Ok(Rect { x, y, width: 39, height: 4 })
|
||||
}
|
||||
|
||||
const KEY_WHITE: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: ::ratatui::style::Modifier::empty(),
|
||||
sub_modifier: ::ratatui::style::Modifier::empty(),
|
||||
};
|
||||
|
||||
const KEY_BLACK: Style = Style {
|
||||
fg: Some(Color::Black),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: ::ratatui::style::Modifier::empty(),
|
||||
sub_modifier: ::ratatui::style::Modifier::empty(),
|
||||
};
|
||||
|
||||
const KEY_HORIZONTAL_STYLE: [Style;12] = [
|
||||
KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
|
||||
KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
|
||||
];
|
||||
|
||||
fn draw_sequencer_vertical (sequencer: &Sequencer, buf: &mut Buffer, area: Rect, beat: u64) -> Usually<Rect> {
|
||||
let Rect { x, y, .. } = area;
|
||||
let transport = sequencer.transport.query().unwrap();
|
||||
let (time0, time1) = sequencer.time_axis;
|
||||
let (note0, note1) = sequencer.note_axis;
|
||||
let bw = Style::default().dim();
|
||||
let bg = Style::default().on_black();
|
||||
for key in note0..note1.max(note0+32) {
|
||||
let x = x + 5 + key - note0;
|
||||
let color = if sequencer.notes_on[key as usize] {
|
||||
Style::default().red()
|
||||
} else {
|
||||
KEY_HORIZONTAL_STYLE[key as usize % 12]
|
||||
};
|
||||
buf.set_string(x, y - 1, &format!("▄"), color);
|
||||
if key % 12 == 0 {
|
||||
let octave = format!("C{}", (key / 12) as i8 - 4);
|
||||
buf.set_string(x, y, &octave, Style::default());
|
||||
}
|
||||
}
|
||||
for step in time0..time1 {
|
||||
let y = y - time0 + step / 2;
|
||||
//buf.set_string(x + 5, y, &" ".repeat(32.max(note1-note0)as usize), bg);
|
||||
if step % 8 == 0 {
|
||||
buf.set_string(x + 2, y, &format!("{:2} ", step + 1), Style::default());
|
||||
}
|
||||
for k in note0..note1.max(note0+32) {
|
||||
let key = ::midly::num::u7::from_int_lossy(k as u8);
|
||||
if step % 2 == 0 {
|
||||
let (a, b, c) = (
|
||||
(step + 0) as u32 * sequencer.ppq,
|
||||
(step + 1) as u32 * sequencer.ppq,
|
||||
(step + 2) as u32 * sequencer.ppq,
|
||||
);
|
||||
let (character, style) = match (
|
||||
contains_note_on(&sequencer.sequence, key, a, b),
|
||||
contains_note_on(&sequencer.sequence, key, b, c),
|
||||
) {
|
||||
(true, true) => ("█", bg),
|
||||
(true, false) => ("▀", bg),
|
||||
(false, true) => ("▄", bg),
|
||||
(false, false) => (" ", bg),
|
||||
};
|
||||
buf.set_string(x + 5 + k - note0, y, character, style);
|
||||
}
|
||||
}
|
||||
if beat == step as u64 {
|
||||
buf.set_string(x + 1, y, if beat % 2 == 0 { "▀" } else { "▄" }, Style::default().yellow());
|
||||
}
|
||||
}
|
||||
buf.set_string(
|
||||
x + 5 + sequencer.note_cursor,
|
||||
y + sequencer.time_cursor / 2,
|
||||
if sequencer.time_cursor % 2 == 0 { "▀" } else { "▄" },
|
||||
Style::default()
|
||||
);
|
||||
Ok(Rect { x, y, width: area.width, height: (time1-time0)/2 })
|
||||
}
|
||||
|
||||
fn contains_note_on (sequence: &Sequence, k: ::midly::num::u7, start: u32, end: u32) -> bool {
|
||||
for (i, (t, events)) in sequence.range(start..end).enumerate() {
|
||||
for event in events.iter() {
|
||||
match event {
|
||||
::midly::MidiMessage::NoteOn {key,..} => {
|
||||
if *key == k {
|
||||
return true
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const KEYS_VERTICAL: [&'static str; 6] = [
|
||||
"▀", "▀", "▀", "█", "▄", "▄",
|
||||
];
|
||||
|
||||
fn draw_sequencer_horizontal (sequencer: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let Rect { x, y, .. } = area;
|
||||
let (time0, time1) = sequencer.time_axis;
|
||||
let (note0, note1) = sequencer.note_axis;
|
||||
let bw = Style::default().dim();
|
||||
let bg = Style::default().on_black();
|
||||
for i in 0..32.max(note1-note0)/2 {
|
||||
let y = y + i;
|
||||
buf.set_string(x + 1, y, KEYS_VERTICAL[(i % 6) as usize], bw);
|
||||
buf.set_string(x + 2, y, "█", bw);
|
||||
buf.set_string(x + 5, y, &" ".repeat((time1 - time0) as usize), bg);
|
||||
if i % 6 == 0 {
|
||||
let octave = format!("C{}", ((note1 - i) / 6) as i8 - 4);
|
||||
buf.set_string(x+5, y, &octave, Style::default());
|
||||
}
|
||||
}
|
||||
for step in time0..time1 {
|
||||
let time_start = step as u32 * sequencer.ppq;
|
||||
let time_end = (step + 1) as u32 * sequencer.ppq;
|
||||
for (i, (t, events)) in sequencer.sequence.range(time_start..time_end).enumerate() {
|
||||
if events.len() > 0 {
|
||||
buf.set_string(x + 5 + step as u16, y, "█", bw);
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.set_string(
|
||||
x + 5 + sequencer.time_cursor,
|
||||
y + sequencer.note_cursor / 2,
|
||||
if sequencer.note_cursor % 2 == 0 { "▀" } else { "▄" },
|
||||
Style::default()
|
||||
);
|
||||
Ok(Rect { x, y, width: time1 - time0 + 6, height: 32.max(note1 - note0) / 2 })
|
||||
}
|
||||
|
||||
//buf.set_string(x + 3, y, "╭1.1.", Style::default().dim());
|
||||
//buf.set_string(x + 3 + 16, y, "╭1.2.", Style::default().dim());
|
||||
//buf.set_string(x + 3 + 32, y, "╭1.3.", Style::default().dim());
|
||||
//buf.set_string(x + 3 + 48, y, "╭1.4.", Style::default().dim());
|
||||
////buf.set_string(x + 2, y, "╭", Style::default().dim());
|
||||
////buf.set_string(x + 2, y + 13, "╰", Style::default().dim());
|
||||
//buf.set_string(x + 2 + 65, y, "╮", Style::default().dim());
|
||||
//buf.set_string(x + 2 + 65, y + 13, "╯", Style::default().dim());
|
||||
//let transport = sequencer.transport.query().unwrap();
|
||||
//let frame = transport.pos.frame();
|
||||
//let rate = transport.pos.frame_rate().unwrap();
|
||||
//let second = (frame as f64) / (rate as f64);
|
||||
//let minute = second / 60f64;
|
||||
//let bpm = 120f64;
|
||||
//let div = 4;
|
||||
//let beats = minute * bpm;
|
||||
//let bars = beats as u32 / div as u32;
|
||||
//let beat = beats as u32 % div as u32 + 1;
|
||||
//let beat_sub = beats % 1.0;
|
||||
//buf.set_string(
|
||||
//x - 18, y + height,
|
||||
//format!("BBT {bars:04}:{beat:02}.{:02}", (beat_sub * 16.0) as u32),
|
||||
//Style::default()
|
||||
//);
|
||||
//let bg = if step as u32 == (beat - 1) * 16 + (beat_sub * 16.0) as u32 {
|
||||
//ratatui::style::Color::Gray
|
||||
//} else if step == sequencer.cursor.1 as usize || key == sequencer.cursor.0/2 {
|
||||
//ratatui::style::Color::Black
|
||||
//} else {
|
||||
//ratatui::style::Color::Reset
|
||||
//};
|
||||
//let top = sequence[(key * 2) as usize][step].is_some();
|
||||
//let bottom = sequence[(key * 2 + 1) as usize][step].is_some();
|
||||
//match (top, bottom) {
|
||||
//(true, true) => {
|
||||
//buf.set_string(x + 3 + step as u16, y + 1 + key, "█",
|
||||
//Style::default().yellow().not_dim().bold().bg(bg));
|
||||
//},
|
||||
//(true, false) => {
|
||||
//buf.set_string(x + 3 + step as u16, y + 1 + key, "▀",
|
||||
//Style::default().yellow().not_dim().bold().bg(bg));
|
||||
//},
|
||||
//(false, true) => {
|
||||
//buf.set_string(x + 3 + step as u16, y + 1 + key, "▄",
|
||||
//Style::default().yellow().not_dim().bold().bg(bg));
|
||||
//},
|
||||
//(false, false) => if step % 16 == 0 {
|
||||
//buf.set_string(x + 3 + step as u16, y + 1 + key, "┊",
|
||||
//Style::default().dim().bg(bg))
|
||||
//} else {
|
||||
//buf.set_string(x + 3 + step as u16, y + 1 + key, "·",
|
||||
//Style::default().dim().bg(bg))
|
||||
//},
|
||||
//}
|
||||
//for step in 0..64 {
|
||||
//if step % 8 == 0 {
|
||||
//buf.set_string(x + 3 + step as u16, y + 1 + 12, [
|
||||
//"|a", "|b", "|c", "|d", "|e", "|f", "|g", "|h"
|
||||
//][step / 8 as usize], Style::default().dim())
|
||||
//}
|
||||
//}
|
||||
|
||||
//{
|
||||
//let mut area = area.clone();
|
||||
//area.y = area.y + 3;
|
||||
//area.x = area.x + 1;
|
||||
//area.height = area.height - 2;
|
||||
//draw_sequence_keys(area, buf,
|
||||
//&sequencer.jack_client.as_client().transport().query().unwrap(),
|
||||
//&sequencer.sequence,
|
||||
//&sequencer.cursor);
|
||||
//draw_sequence_cursor(area, buf,
|
||||
//&sequencer.cursor);
|
||||
//}
|
||||
//draw_box(buf, Rect { x, y, width: 18, height });
|
||||
|
||||
//draw_box(buf, Rect { x, y, width: 40, height: 8 });
|
||||
//buf.set_string(x + 1, y + 3,
|
||||
//&format!(" │ INPUT │ OUTPUT │"),
|
||||
//Style::default().gray());
|
||||
//buf.set_string(x + 1, y + 5,
|
||||
//&format!(" {} │ IN │ ⏺ REC │ ⏺ DUB │ ▶ PLAY │ ⏹ STOP │ OUT │ 00:00.00 / 00:00.00", &sequencer.name),
|
||||
//Style::default().gray());
|
||||
//if sequencer.inputs_open.fetch_and(true, Ordering::Relaxed) {
|
||||
//buf.set_string(x + 15, y + 1, "IN", Style::default().green().bold());
|
||||
//}
|
||||
//if sequencer.outputs_open.fetch_and(true, Ordering::Relaxed) {
|
||||
//buf.set_string(x + 54, y + 1, "OUT", Style::default().green().bold());
|
||||
//}
|
||||
|
||||
//let mut command = |y2: u16, c: &str, ommand: &str, value: &str| {
|
||||
//buf.set_string(x + 1, y + y2, c, Style::default().bold());
|
||||
//buf.set_string(x + 2, y + y2, ommand, Style::default().dim());
|
||||
//buf.set_string(x + 4 + ommand.len() as u16, y + y2, value, Style::default().bold());
|
||||
//};
|
||||
//for (y, c, ommand, value) in [
|
||||
////(5, "I", "nputs", "[+]"),
|
||||
////(6, "O", "utputs", "[+]"),
|
||||
////(7, "C", "hannel", "01"),
|
||||
//(8, "G", "rid", "1/16"),
|
||||
//(9, "Z", "oom", "1/64"),
|
||||
//(10, "R", "ate", "1/1"),
|
||||
//(11, "S", "ync", "1 bar"),
|
||||
//(12, "A", "dd note", ""),
|
||||
//(13, "D", "elete note", ""),
|
||||
//] {
|
||||
//command(y, c, ommand, value)
|
||||
//}
|
||||
//draw_box(buf, Rect { x, y: y + height - 1, width, height: 3 });
|
||||
//buf.set_string(x + 1, y + height,
|
||||
//&format!(" Grid 1/16 │ Zoom 1/64 │ Rate 1/1 │ Sync 1 bar │ Add note │ Delete note "),
|
||||
//Style::default().gray());
|
||||
//buf.set_string(x + 1, y + 1, &format!(" ▶ {} ", &sequencer.name),
|
||||
//Style::default().white().bold().not_dim());
|
||||
//buf.set_string(x + 4, y + 0, "┬", Style::default().gray());
|
||||
//buf.set_string(x + 4, y + 1, "│", Style::default().gray().dim());
|
||||
//buf.set_string(x + 4, y + 2, "┴", Style::default().gray());
|
||||
//buf.set_string(x + 21, y + 1, " ⏺ REC DUB ",
|
||||
//Style::default().white().bold().not_dim());
|
||||
//buf.set_string(x + 18, y + 0, "┬", Style::default().gray());
|
||||
//buf.set_string(x + 18, y + 1, "│", Style::default().gray().dim());
|
||||
//buf.set_string(x + 18, y + 2, "┴", Style::default().gray());
|
||||
|
||||
//draw_rec_dub_button(buf, x + 17, y);
|
||||
//draw_sequence_button(buf, x, y, &sequencer.name);
|
||||
|
||||
//draw_leaf(buf, Rect { x, y: y + height, width, height: 2 }, 0, 0, "Inputs ");
|
||||
//draw_leaf(buf, Rect { x, y: y + height, width, height: 2 }, 0, 8, "Outputs");
|
||||
//for x2 in 0..4 {
|
||||
//for y2 in 0..4 {
|
||||
//buf.set_string(
|
||||
//x + 2 + x2 * 3,
|
||||
//y + 5 + y2,
|
||||
//format!("{:>02} ", 1 + x2 + y2 * 4),
|
||||
//Style::default().green().bold().not_dim()
|
||||
//)
|
||||
//}
|
||||
//}
|
||||
//
|
||||
//fn draw_sequence_button (
|
||||
//buf: &mut Buffer,
|
||||
//x: u16,
|
||||
//y: u16,
|
||||
//name: &str
|
||||
//) {
|
||||
//draw_box(buf, Rect { x, y, width: 18, height: 3 });
|
||||
//buf.set_string(x + 1, y + 1, &format!(" ▶ {} ", name),
|
||||
//Style::default().white().bold().not_dim());
|
||||
//buf.set_string(x + 4, y + 0, "┬", Style::default().gray());
|
||||
//buf.set_string(x + 4, y + 1, "│", Style::default().gray().dim());
|
||||
//buf.set_string(x + 4, y + 2, "┴", Style::default().gray());
|
||||
//}
|
||||
|
||||
//fn draw_rec_dub_button (
|
||||
//buf: &mut Buffer,
|
||||
//x: u16,
|
||||
//y: u16,
|
||||
//) {
|
||||
//draw_box(buf, Rect { x, y, width: 18, height: 3 });
|
||||
//buf.set_string(x + 1, y + 1, " ⏺ REC DUB ",
|
||||
//Style::default().white().bold().not_dim());
|
||||
//buf.set_string(x + 5, y + 0, "┬", Style::default().gray());
|
||||
//buf.set_string(x + 5, y + 1, "│", Style::default().gray().dim());
|
||||
//buf.set_string(x + 5, y + 2, "┴", Style::default().gray());
|
||||
//buf.set_string(x + 11, y + 0, "┬", Style::default().gray());
|
||||
//buf.set_string(x + 11, y + 1, "│", Style::default().gray().dim());
|
||||
//buf.set_string(x + 11, y + 2, "┴", Style::default().gray());
|
||||
//}
|
||||
|
|
|
|||
14
src/time.rs
14
src/time.rs
|
|
@ -8,7 +8,7 @@ pub struct Frame(pub u32);
|
|||
pub struct Usec(pub u64);
|
||||
|
||||
/// Beats per minute as `120000` = 120.000BPM
|
||||
pub struct Tempo(pub u32);
|
||||
pub struct Tempo(pub u64);
|
||||
|
||||
/// Time signature: N/Mths per bar.
|
||||
pub struct Signature(pub u32, pub u32);
|
||||
|
|
@ -36,17 +36,17 @@ impl Usec {
|
|||
}
|
||||
|
||||
impl Tempo {
|
||||
#[inline]
|
||||
pub fn usec_per_bar (&self, beats: u64) -> Usec {
|
||||
Usec(beats * self.usec_per_beat().0)
|
||||
}
|
||||
#[inline]
|
||||
pub fn usec_per_beat (&self) -> Usec {
|
||||
Usec(60_000_000_000 / self.0 as u64)
|
||||
}
|
||||
#[inline]
|
||||
pub fn usec_per_quarter (&self) -> Usec {
|
||||
Usec(self.usec_per_beat().0 / 4)
|
||||
}
|
||||
#[inline]
|
||||
pub fn usec_per_tick (&self, ppq: u32) -> Usec {
|
||||
Usec(self.usec_per_quarter().0 / ppq as u64)
|
||||
pub fn usec_per_step (&self, divisor: u64) -> Usec {
|
||||
Usec(self.usec_per_beat().0 / divisor as u64)
|
||||
}
|
||||
#[inline]
|
||||
pub fn quantize (&self, step: &NoteDuration, time: Usec) -> Usec {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue