record into sequencer from midi in!

This commit is contained in:
🪞👃🪞 2024-06-16 20:41:13 +03:00
parent 2e7252f8b9
commit 2118615aea
2 changed files with 94 additions and 53 deletions

View file

@ -1,6 +1,8 @@
use crate::prelude::*;
use ratatui::style::Stylize;
type Sequence = std::collections::BTreeMap<u32, Vec<::midly::MidiMessage>>;
pub struct Sequencer {
name: String,
mode: SequencerView,
@ -11,7 +13,7 @@ pub struct Sequencer {
rate: Hz,
tempo: Tempo,
transport: ::jack::Transport,
sequence: std::collections::BTreeMap<u32, Vec<Vec<u8>>>,
sequence: Sequence,
ppq: u32,
input_port: Port<MidiIn>,
input_connect: Vec<String>,
@ -64,14 +66,33 @@ 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;
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
state.notes_on[key.as_int() as usize] = true;
if state.sequence.contains_key(&time) {
state.sequence.get_mut(&time).unwrap().push(message.clone());
} else {
state.sequence.insert(time, vec![message.clone()]);
}
},
midly::MidiMessage::NoteOff { key, vel } => {
state.notes_on[key.as_int() as usize] = false
state.notes_on[key.as_int() as usize] = false;
if state.sequence.contains_key(&time) {
state.sequence.get_mut(&time).unwrap().push(message.clone());
} else {
state.sequence.insert(time, vec![message.clone()]);
}
},
_ => {}
},
@ -152,13 +173,14 @@ const COMMANDS: [(KeyCode, &'static str, &'static str, &'static dyn Fn(&mut Sequ
fn nop (_: &mut Sequencer) {
}
fn note_add (s: &mut Sequencer) {
let time = (s.time_axis.0 + s.time_cursor) as u32;
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 note_on = vec![0, 0, 0];
let note_off = vec![0, 0, 0];
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() };
if s.sequence.contains_key(&time_start) {
s.sequence.get_mut(&time_start).unwrap().push(note_off.clone());
s.sequence.get_mut(&time_start).unwrap().push(note_on.clone());
} else {
s.sequence.insert(time_start, vec![note_on]);
}
@ -234,7 +256,17 @@ fn render (sequencer: &Sequencer, buf: &mut Buffer, mut area: Rect)
let Rect { x, y, width, height } = area;
let (time0, time1) = sequencer.time_axis;
let (note0, note1) = sequencer.note_axis;
let header = draw_sequencer_header(sequencer, buf, area)?;
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 },
@ -245,7 +277,7 @@ fn render (sequencer: &Sequencer, buf: &mut Buffer, mut area: Rect)
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,
@ -261,10 +293,10 @@ fn render (sequencer: &Sequencer, buf: &mut Buffer, mut area: Rect)
}))
}
fn draw_sequencer_header (sequencer: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
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!("00:00.00 / 00:00.00"), style);
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 {
@ -306,8 +338,9 @@ const KEY_HORIZONTAL_STYLE: [Style;12] = [
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) -> Usually<Rect> {
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();
@ -327,41 +360,33 @@ fn draw_sequencer_vertical (sequencer: &Sequencer, buf: &mut Buffer, area: Rect)
}
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);
//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());
}
if step % 2 == 0 {
let mut has_1 = false;
let (start, end) = (
(step + 0) as u32 * sequencer.ppq,
(step + 1) as u32 * sequencer.ppq);
for (i, (t, events)) in sequencer.sequence.range(start..end).enumerate() {
if events.len() > 0 {
has_1 = true;
break
}
}
let mut has_2 = false;
let (start, end) = (
(step + 1) as u32 * sequencer.ppq,
(step + 2) as u32 * sequencer.ppq
);
for (i, (t, events)) in sequencer.sequence.range(start..end).enumerate() {
if events.len() > 0 {
has_2 = true;
break
}
}
if has_1 || has_2 {
buf.set_string(x + 4, y, &match (has_1, has_2) {
(true, true) => "",
(true, false) => "",
(false, true) => "",
_ => " ",
}, 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,
@ -372,6 +397,22 @@ fn draw_sequencer_vertical (sequencer: &Sequencer, buf: &mut Buffer, area: Rect)
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] = [
"", "", "", "", "", "",
];

View file

@ -5,7 +5,7 @@ pub struct Hz(pub u32);
pub struct Frame(pub u32);
/// One microsecond.
pub struct Usec(pub u32);
pub struct Usec(pub u64);
/// Beats per minute as `120000` = 120.000BPM
pub struct Tempo(pub u32);
@ -24,29 +24,29 @@ pub enum NoteDuration {
impl Frame {
#[inline]
pub fn to_usec (&self, rate: &Hz) -> Usec {
Usec((self.0 * 1000) / rate.0)
Usec((self.0 as u64 * 1000000) / rate.0 as u64)
}
}
impl Usec {
#[inline]
pub fn to_frame (&self, rate: &Hz) -> Frame {
Frame((self.0 * rate.0) / 1000)
Frame(((self.0 * rate.0 as u64) / 1000) as u32)
}
}
impl Tempo {
#[inline]
pub fn to_usec_per_beat (&self) -> Usec {
Usec((60_000_000_000 / self.0 as usize) as u32)
pub fn usec_per_beat (&self) -> Usec {
Usec(60_000_000_000 / self.0 as u64)
}
#[inline]
pub fn to_usec_per_quarter (&self) -> Usec {
Usec(self.to_usec_per_quarter().0 / 4)
pub fn usec_per_quarter (&self) -> Usec {
Usec(self.usec_per_beat().0 / 4)
}
#[inline]
pub fn to_usec_per_tick (&self, ppq: u32) -> Usec {
Usec(self.to_usec_per_quarter().0 / ppq)
pub fn usec_per_tick (&self, ppq: u32) -> Usec {
Usec(self.usec_per_quarter().0 / ppq as u64)
}
#[inline]
pub fn quantize (&self, step: &NoteDuration, time: Usec) -> Usec {
@ -69,11 +69,11 @@ impl NoteDuration {
fn to_usec (&self, bpm: &Tempo) -> Usec {
Usec(match self {
Self::Nth(time, flies) =>
bpm.to_usec_per_beat().0 * *time as u32 / *flies as u32,
bpm.usec_per_beat().0 * *time as u64 / *flies as u64,
Self::Dotted(duration) =>
duration.to_usec(bpm).0 * 3 / 2,
Self::Tuplet(n, duration) =>
duration.to_usec(bpm).0 * 2 / *n as u32,
duration.to_usec(bpm).0 * 2 / *n as u64,
})
}
fn to_frame (&self, bpm: &Tempo, rate: &Hz) -> Frame {