key lights up in response to recording

This commit is contained in:
🪞👃🪞 2024-06-16 21:42:09 +03:00
parent 2118615aea
commit 674f95fcd2
2 changed files with 254 additions and 396 deletions

View file

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

View file

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