mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 20:56:43 +01:00
record into sequencer from midi in!
This commit is contained in:
parent
2e7252f8b9
commit
2118615aea
2 changed files with 94 additions and 53 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use ratatui::style::Stylize;
|
use ratatui::style::Stylize;
|
||||||
|
|
||||||
|
type Sequence = std::collections::BTreeMap<u32, Vec<::midly::MidiMessage>>;
|
||||||
|
|
||||||
pub struct Sequencer {
|
pub struct Sequencer {
|
||||||
name: String,
|
name: String,
|
||||||
mode: SequencerView,
|
mode: SequencerView,
|
||||||
|
|
@ -11,7 +13,7 @@ pub struct Sequencer {
|
||||||
rate: Hz,
|
rate: Hz,
|
||||||
tempo: Tempo,
|
tempo: Tempo,
|
||||||
transport: ::jack::Transport,
|
transport: ::jack::Transport,
|
||||||
sequence: std::collections::BTreeMap<u32, Vec<Vec<u8>>>,
|
sequence: Sequence,
|
||||||
ppq: u32,
|
ppq: u32,
|
||||||
input_port: Port<MidiIn>,
|
input_port: Port<MidiIn>,
|
||||||
input_connect: Vec<String>,
|
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) {
|
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) {
|
for event in state.input_port.iter(scope) {
|
||||||
match midly::live::LiveEvent::parse(event.bytes).unwrap() {
|
match midly::live::LiveEvent::parse(event.bytes).unwrap() {
|
||||||
midly::live::LiveEvent::Midi { channel, message } => match message {
|
midly::live::LiveEvent::Midi { channel, message } => match message {
|
||||||
midly::MidiMessage::NoteOn { key, vel } => {
|
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 } => {
|
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()]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
|
@ -155,10 +176,11 @@ 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_start = time * s.ppq;
|
||||||
let time_end = (time + 1) * s.ppq;
|
let time_end = (time + 1) * s.ppq;
|
||||||
let note_on = vec![0, 0, 0];
|
let key = ::midly::num::u7::from_int_lossy((s.note_cursor + s.note_axis.0) as u8);
|
||||||
let note_off = vec![0, 0, 0];
|
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) {
|
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 {
|
} else {
|
||||||
s.sequence.insert(time_start, vec![note_on]);
|
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 Rect { x, y, width, height } = area;
|
||||||
let (time0, time1) = sequencer.time_axis;
|
let (time0, time1) = sequencer.time_axis;
|
||||||
let (note0, note1) = sequencer.note_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 {
|
let piano = match sequencer.mode {
|
||||||
SequencerView::Tiny =>
|
SequencerView::Tiny =>
|
||||||
Rect { x, y, width, height: 0 },
|
Rect { x, y, width, height: 0 },
|
||||||
|
|
@ -245,7 +277,7 @@ fn render (sequencer: &Sequencer, buf: &mut Buffer, mut area: Rect)
|
||||||
y: y + header.height,
|
y: y + header.height,
|
||||||
width: 3 + note1 - note0,
|
width: 3 + note1 - note0,
|
||||||
height: 3 + time1 - time0,
|
height: 3 + time1 - time0,
|
||||||
})?,
|
}, beats)?,
|
||||||
SequencerView::Horizontal => draw_sequencer_horizontal(sequencer, buf, Rect {
|
SequencerView::Horizontal => draw_sequencer_horizontal(sequencer, buf, Rect {
|
||||||
x,
|
x,
|
||||||
y: y + header.height,
|
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 Rect { x, y, width, height } = area;
|
||||||
let style = Style::default().gray();
|
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 + 2, y + 1, &format!("{}", &sequencer.name), style.white().bold());
|
||||||
buf.set_string(x + 1, y + 2, &format!(" ▶ PLAY │ ⏹ STOP │ │"), style);
|
buf.set_string(x + 1, y + 2, &format!(" ▶ PLAY │ ⏹ STOP │ │"), style);
|
||||||
buf.set_string(x + 2, y + 2, &format!("▶ PLAY"), if sequencer.playing {
|
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,
|
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 Rect { x, y, .. } = area;
|
||||||
|
let transport = sequencer.transport.query().unwrap();
|
||||||
let (time0, time1) = sequencer.time_axis;
|
let (time0, time1) = sequencer.time_axis;
|
||||||
let (note0, note1) = sequencer.note_axis;
|
let (note0, note1) = sequencer.note_axis;
|
||||||
let bw = Style::default().dim();
|
let bw = Style::default().dim();
|
||||||
|
|
@ -327,40 +360,32 @@ fn draw_sequencer_vertical (sequencer: &Sequencer, buf: &mut Buffer, area: Rect)
|
||||||
}
|
}
|
||||||
for step in time0..time1 {
|
for step in time0..time1 {
|
||||||
let y = y - time0 + step / 2;
|
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 {
|
if step % 8 == 0 {
|
||||||
buf.set_string(x + 2, y, &format!("{:2} ", step + 1), Style::default());
|
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 {
|
if step % 2 == 0 {
|
||||||
let mut has_1 = false;
|
let (a, b, c) = (
|
||||||
let (start, end) = (
|
|
||||||
(step + 0) as u32 * sequencer.ppq,
|
(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 + 1) as u32 * sequencer.ppq,
|
||||||
(step + 2) as u32 * sequencer.ppq
|
(step + 2) as u32 * sequencer.ppq,
|
||||||
);
|
);
|
||||||
for (i, (t, events)) in sequencer.sequence.range(start..end).enumerate() {
|
let (character, style) = match (
|
||||||
if events.len() > 0 {
|
contains_note_on(&sequencer.sequence, key, a, b),
|
||||||
has_2 = true;
|
contains_note_on(&sequencer.sequence, key, b, c),
|
||||||
break
|
) {
|
||||||
|
(true, true) => ("█", bg),
|
||||||
|
(true, false) => ("▀", bg),
|
||||||
|
(false, true) => ("▄", bg),
|
||||||
|
(false, false) => (" ", bg),
|
||||||
|
};
|
||||||
|
buf.set_string(x + 5 + k - note0, y, character, style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if has_1 || has_2 {
|
if beat == step as u64 {
|
||||||
buf.set_string(x + 4, y, &match (has_1, has_2) {
|
buf.set_string(x + 1, y, if beat % 2 == 0 { "▀" } else { "▄" }, Style::default().yellow());
|
||||||
(true, true) => "█",
|
|
||||||
(true, false) => "▀",
|
|
||||||
(false, true) => "▄",
|
|
||||||
_ => " ",
|
|
||||||
}, Style::default());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf.set_string(
|
buf.set_string(
|
||||||
|
|
@ -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 })
|
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] = [
|
const KEYS_VERTICAL: [&'static str; 6] = [
|
||||||
"▀", "▀", "▀", "█", "▄", "▄",
|
"▀", "▀", "▀", "█", "▄", "▄",
|
||||||
];
|
];
|
||||||
|
|
|
||||||
22
src/time.rs
22
src/time.rs
|
|
@ -5,7 +5,7 @@ pub struct Hz(pub u32);
|
||||||
pub struct Frame(pub u32);
|
pub struct Frame(pub u32);
|
||||||
|
|
||||||
/// One microsecond.
|
/// One microsecond.
|
||||||
pub struct Usec(pub u32);
|
pub struct Usec(pub u64);
|
||||||
|
|
||||||
/// Beats per minute as `120000` = 120.000BPM
|
/// Beats per minute as `120000` = 120.000BPM
|
||||||
pub struct Tempo(pub u32);
|
pub struct Tempo(pub u32);
|
||||||
|
|
@ -24,29 +24,29 @@ pub enum NoteDuration {
|
||||||
impl Frame {
|
impl Frame {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_usec (&self, rate: &Hz) -> Usec {
|
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 {
|
impl Usec {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_frame (&self, rate: &Hz) -> Frame {
|
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 {
|
impl Tempo {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_usec_per_beat (&self) -> Usec {
|
pub fn usec_per_beat (&self) -> Usec {
|
||||||
Usec((60_000_000_000 / self.0 as usize) as u32)
|
Usec(60_000_000_000 / self.0 as u64)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_usec_per_quarter (&self) -> Usec {
|
pub fn usec_per_quarter (&self) -> Usec {
|
||||||
Usec(self.to_usec_per_quarter().0 / 4)
|
Usec(self.usec_per_beat().0 / 4)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_usec_per_tick (&self, ppq: u32) -> Usec {
|
pub fn usec_per_tick (&self, ppq: u32) -> Usec {
|
||||||
Usec(self.to_usec_per_quarter().0 / ppq)
|
Usec(self.usec_per_quarter().0 / ppq as u64)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn quantize (&self, step: &NoteDuration, time: Usec) -> Usec {
|
pub fn quantize (&self, step: &NoteDuration, time: Usec) -> Usec {
|
||||||
|
|
@ -69,11 +69,11 @@ impl NoteDuration {
|
||||||
fn to_usec (&self, bpm: &Tempo) -> Usec {
|
fn to_usec (&self, bpm: &Tempo) -> Usec {
|
||||||
Usec(match self {
|
Usec(match self {
|
||||||
Self::Nth(time, flies) =>
|
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) =>
|
Self::Dotted(duration) =>
|
||||||
duration.to_usec(bpm).0 * 3 / 2,
|
duration.to_usec(bpm).0 * 3 / 2,
|
||||||
Self::Tuplet(n, duration) =>
|
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 {
|
fn to_frame (&self, bpm: &Tempo, rate: &Hz) -> Frame {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue