mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
tabula rasa
This commit is contained in:
commit
11a9f3ba50
33 changed files with 1937 additions and 0 deletions
114
src/sequence/midi.rs
Normal file
114
src/sequence/midi.rs
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
use crate::sequence::{Frame, Time};
|
||||
|
||||
/// Pulses per quarter note
|
||||
pub const PPQ: usize = 96;
|
||||
|
||||
pub struct Message {
|
||||
bytes: Vec<u8>
|
||||
}
|
||||
|
||||
pub struct MIDISequence {
|
||||
name: String,
|
||||
length: usize,
|
||||
events: Vec<(Frame, MIDIEvent)>,
|
||||
notes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl MIDISequence {
|
||||
pub fn new (name: &str, length: usize) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
length,
|
||||
events: vec![],
|
||||
notes: vec![],
|
||||
}
|
||||
}
|
||||
pub fn add (&mut self, time: usize, event: MIDIEvent) -> &mut Self {
|
||||
let mut sort = false;
|
||||
match &event {
|
||||
MIDIEvent::NoteOn(pitch, _velocity) => {
|
||||
if !self.notes.contains(pitch) {
|
||||
self.notes.push(*pitch);
|
||||
sort = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if sort {
|
||||
self.notes.sort();
|
||||
}
|
||||
self.events.push((time, event));
|
||||
self
|
||||
}
|
||||
pub fn render (&self, stdout: &mut std::io::Stdout, resolution: usize)
|
||||
-> Result<(), Box<dyn std::error::Error>>
|
||||
{
|
||||
use crossterm::{*, style::Stylize};
|
||||
let (col, row) = cursor::position()?;
|
||||
let unit = PPQ / resolution;
|
||||
let length = self.length / unit;
|
||||
let mut header = String::from(" ");
|
||||
for i in 0..length {
|
||||
if i % 8 == 0 {
|
||||
header.push('┍');
|
||||
} else {
|
||||
header.push('━');
|
||||
}
|
||||
}
|
||||
header.push('┑');
|
||||
let mut tracks: Vec<(String, String)> = vec![];
|
||||
for note in self.notes.iter().rev() {
|
||||
let mut row_header = format!(" {note:3} ");
|
||||
let mut row = String::new();
|
||||
for beat in 0..length {
|
||||
let mut active = false;
|
||||
for (frame, event) in self.events.iter() {
|
||||
match event {
|
||||
MIDIEvent::NoteOn(pitch, _) => {
|
||||
if pitch == note
|
||||
&& *frame >= beat * unit
|
||||
&& *frame < (beat + 1) * unit
|
||||
{
|
||||
active = true;
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if active {
|
||||
row.push('⯀')
|
||||
} else if beat == 0 {
|
||||
row.push('│');
|
||||
} else if beat % 8 == 0 {
|
||||
row.push('┆');
|
||||
} else {
|
||||
row.push('·');
|
||||
}
|
||||
}
|
||||
tracks.push((row_header, row));
|
||||
}
|
||||
stdout.queue(style::PrintStyledContent(header.grey()))?;
|
||||
for (row_header, row) in tracks.into_iter() {
|
||||
stdout
|
||||
.queue(cursor::MoveDown(1))?
|
||||
.queue(cursor::MoveToColumn(col))?
|
||||
.queue(style::PrintStyledContent(row_header.grey()))?
|
||||
.queue(style::PrintStyledContent(row.clone().white()))?
|
||||
.queue(style::PrintStyledContent(row.clone().yellow()))?
|
||||
.queue(style::PrintStyledContent(row.white()))?;
|
||||
}
|
||||
//stdout
|
||||
//.queue(cursor::MoveDown(1))?
|
||||
//.queue(cursor::MoveToColumn(col))?
|
||||
//.queue(style::PrintStyledContent(footer.grey()))?;
|
||||
//.queue(style::PrintStyledContent("················".grey()))?
|
||||
//.queue(style::PrintStyledContent("│".yellow()))?
|
||||
//.queue(cursor::MoveDown(1))?
|
||||
//.queue(cursor::MoveLeft(18))?
|
||||
//.queue(style::PrintStyledContent("└────────────────┘".yellow()))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub enum MIDIEvent {
|
||||
NoteOn(u8, u8)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue