bye sequencer

This commit is contained in:
🪞👃🪞 2024-07-03 18:36:16 +03:00
parent 2165e5d45d
commit 316fe45b2a
12 changed files with 510 additions and 759 deletions

View file

@ -1,13 +1,22 @@
use crate::{core::*,model::*,view::*};
pub mod horizontal;
pub mod vertical;
#[derive(Debug, Clone)]
pub enum SequencerMode { Tiny, Compact, Horizontal, Vertical, }
pub struct SequencerView<'a> {
pub focused: bool,
pub phrase: Option<&'a Phrase>,
pub track: Option<&'a Track>,
pub ppq: usize,
/// Range of notes to display
pub note_start: usize,
/// Position of cursor within note range
pub note_cursor: usize,
/// PPQ per display unit
pub time_zoom: usize,
/// Range of time steps to display
pub time_start: usize,
/// Position of cursor within time range
pub time_cursor: usize,
}
impl<'a> Render for SequencerView<'a> {
@ -18,87 +27,366 @@ impl<'a> Render for SequencerView<'a> {
lozenge_left(buf, x, y, height, style);
lozenge_right(buf, x + width - 1, y, height, style);
}
if let Some(ref track) = self.track {
self::horizontal::draw(
buf,
area,
self.phrase,
self.ppq,
track.sequencer.time_cursor,
track.sequencer.time_start,
track.sequencer.time_zoom,
track.sequencer.note_cursor,
track.sequencer.note_start,
Some(if self.focused {
Style::default().green().not_dim()
} else {
Style::default().green().dim()
})
)?;
}
self::horizontal::draw(
buf,
area,
self.phrase,
self.ppq,
self.time_cursor,
self.time_start,
self.time_zoom,
self.note_cursor,
self.note_start,
Some(if self.focused {
Style::default().green().not_dim()
} else {
Style::default().green().dim()
})
)?;
Ok(area)
}
}
pub fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, width, height } = area;
let header = draw_header(s, buf, area)?;
let piano = match s.view {
SequencerMode::Tiny => Rect { x, y, width, height: 0 },
SequencerMode::Compact => Rect { x, y, width, height: 0 },
SequencerMode::Vertical => self::vertical::draw(s, buf, Rect {
x, y: y + header.height, width, height,
})?,
SequencerMode::Horizontal => self::horizontal::draw(
buf,
Rect { x, y: y + header.height, width, height, },
s.phrase(),
s.timebase.ppq() as usize,
s.time_cursor,
s.time_start,
s.time_zoom,
s.note_cursor,
s.note_start,
None
)?,
};
Ok(draw_box(buf, Rect {
x, y,
width: header.width.max(piano.width),
height: header.height + piano.height
}))
}
mod horizontal {
use crate::core::*;
use super::*;
pub fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, width, .. } = area;
let style = Style::default().gray();
crate::view::TransportView {
timebase: &s.timebase,
playing: s.playing,
record: s.recording,
overdub: s.overdub,
monitor: s.monitoring,
frame: 0
}.render(buf, area)?;
let separator = format!("{}", "-".repeat((width - 2).into()));
separator.blit(buf, x, y + 2, Some(style.dim()));
let _ = draw_clips(s, buf, area)?;
Ok(Rect { x, y, width, height: 3 })
}
pub fn draw_clips (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, .. } = area;
let style = Style::default().gray();
for (i, sequence) in s.phrases.iter().enumerate() {
let label = format!("{}", &sequence.name);
label.blit(buf, x + 2, y + 3 + (i as u16)*2, Some(if Some(i) == s.sequence {
match s.playing {
TransportState::Rolling => style.white().bold(),
_ => style.not_dim().bold()
}
} else {
style.dim()
}));
pub fn draw (
buf: &mut Buffer,
area: Rect,
phrase: Option<&Phrase>,
ppq: usize,
time: usize,
time0: usize,
time_z: usize,
note: usize,
note0: usize,
style: Option<Style>,
) -> Usually<Rect> {
let now = 0;
let notes = &[];
match time_z {
1 => "1/384",
2 => "1/192",
3 => "1/128",
4 => "1/96",
6 => "1/64",
8 => "1/48",
12 => "1/32",
16 => "1/24",
24 => "1/16",
32 => "1/12",
48 => "1/8",
64 => "1/6",
96 => "1/4",
128 => "1/3",
192 => "1/2",
384 => "1/1",
_ => ""
}.blit(buf, area.x, area.y, Some(Style::default().dim()));
keys(buf, area, note0, notes)?;
timer(buf, area, time0, now);
if let Some(phrase) = phrase {
lanes(buf, area, phrase, ppq, time_z, time0, note0);
}
let style = style.unwrap_or_else(||{Style::default().green().not_dim()});
cursor(buf, area, style, time, note);
//footer(buf, area, note0, note, time0, time, time_z);
Ok(area)
}
pub fn timer (buf: &mut Buffer, area: Rect, time0: usize, now: usize) {
let x = area.x + 5;
for step in time0..(time0+area.width as usize).saturating_sub(5) {
buf.set_string(x + step as u16, area.y, &"-", if step == now {
Style::default().yellow().bold().not_dim()
} else {
Style::default()
});
}
}
pub fn keys (buf: &mut Buffer, area: Rect, note0: usize, _notes: &[bool])
-> Usually<Rect>
{
let bw = Style::default().dim();
let Rect { x, y, width, height } = area;
let h = height.saturating_sub(2);
for index in 0..h {
let y = y + h - index;
let key = KEYS_VERTICAL[(index % 6) as usize];
key.blit(buf, x + 1, y, Some(bw));
"".blit(buf, x + 2, y, Some(bw));
"|---".repeat(width.saturating_sub(6) as usize).blit(buf, x + 5, y, Some(bw.black()));
let note_a = note0 + (index * 2) as usize;
if note_a % 12 == 0 {
let octave = format!("C{}", (note_a / 12) as i8 - 2);
octave.blit(buf, x + 3, y, None);
continue
}
let note_b = note0 + (index * 2) as usize;
if note_b % 12 == 0 {
let octave = format!("C{}", (note_b / 12) as i8 - 2);
octave.blit(buf, x + 3, y, None);
continue
}
}
Ok(area)
}
pub fn lanes (
buf: &mut Buffer,
area: Rect,
phrase: &Phrase,
ppq: usize,
time_z: usize,
_time0: usize,
note0: usize,
) {
let Rect { x, y, width, height } = area;
//let time0 = time0 / time_z;
//let time1 = time0 + width as usize;
//let note1 = note0 + height as usize;
let bg = Style::default();
let (bw, wh) = (bg.dim(), bg.white());
let offset = 5;
for x in x+offset..x+width-offset {
let step = (x-offset) as usize * time_z;
if step % ppq == 0 {
"|".blit(buf, x as u16, y, Some(Style::default().dim()));
}
let bar = 4 * ppq;
if step % bar == 0 {
format!("{}", (step/bar)+1)
.blit(buf, x as u16, y, Some(Style::default().bold().not_dim()));
}
let (a, b) = (step, step + time_z);
for index in 0..height-2 {
let note_a = note0 + index as usize * 2;
let note_b = note0 + index as usize * 2 + 1;
let (character, style) = match (
phrase.contains_note_on(u7::from_int_lossy(note_a as u8), a, b),
phrase.contains_note_on(u7::from_int_lossy(note_b as u8), a, b),
) {
(true, true) => ("", wh),
(false, true) => ("", wh),
(true, false) => ("", wh),
(false, false) => ("·", bw),
};
let y = y + height.saturating_sub(index+2) as u16;
character.blit(buf, x, y, Some(style));
}
}
}
pub fn cursor (
buf: &mut Buffer,
area: Rect,
style: Style,
time: usize,
note: usize
) {
let x = area.x + 5 + time as u16;
let y = area.y + 1 + note as u16 / 2;
let c = if note % 2 == 0 { "" } else { "" };
c.blit(buf, x, y, Some(style));
}
pub fn footer (
buf: &mut Buffer,
area: Rect,
note0: usize,
note: usize,
time0: usize,
time: usize,
time_z: usize,
) {
let Rect { mut x, y, width, height } = area;
buf.set_string(x, y + height, format!("{}", "-".repeat((width - 2).into())),
Style::default().dim());
buf.set_string(x, y + height + 2, format!("{}", "-".repeat((width - 2).into())),
Style::default().dim());
x = x + 2;
{
for (_, [letter, title, value]) in [
["S", &format!("ync"), &format!("<4/4>")],
["Q", &format!("uant"), &format!("<1/{}>", 4 * time_z)],
["N", &format!("ote"), &format!("{} ({}-{})", note0 + note, note0, "X")],
["T", &format!("ime"), &format!("{} ({}-{})", time0 + time, time0 + 1, "X")],
].iter().enumerate() {
buf.set_string(x, y + height + 1, letter, Style::default().bold().yellow().dim());
x = x + 1;
buf.set_string(x, y + height + 1, &title, Style::default().bold().dim());
x = x + title.len() as u16 + 1;
buf.set_string(x, y + height + 1, &value, Style::default().not_dim());
x = x + value.len() as u16;
buf.set_string(x, y + height + 1, " ", Style::default().dim());
x = x + 2;
}
}
}
Ok(Rect { x, y, width: 14, height: 14 })
}
//mod vertical {
//use super::*;
//pub fn draw (
//s: &Sequencer,
//buf: &mut Buffer,
//mut area: Rect,
//) -> Usually<Rect> {
//area.x = area.x + 13;
//keys(s, buf, area, 0);
//steps(s, buf, area, 0);
//playhead(s, buf, area.x, area.y);
//Ok(area)
//}
//pub fn steps (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) {
//if s.sequence.is_none() {
//return
//}
//let ppq = s.timebase.ppq() as usize;
//let bg = Style::default();
//let bw = bg.dim();
//let wh = bg.white();
//let Rect { x, y, .. } = area;
//for step in s.time_start..s.time_start+area.height as usize {
//let y = y - (s.time_start + step / 2) as u16;
//let step = step as usize;
////buf.set_string(x + 5, y, &" ".repeat(32.max(note1-s.note_start)as usize), bg);
//if step % s.time_zoom == 0 {
//buf.set_string(x + 2, y, &format!("{:2} ", step + 1), Style::default());
//}
//for k in s.note_start..s.note_start+area.width as usize {
//let key = ::midly::num::u7::from_int_lossy(k as u8);
//if step % 2 == 0 {
//let (a, b, c) = (
//(step + 0) as usize * ppq / s.time_zoom as usize,
//(step + 1) as usize * ppq / s.time_zoom as usize,
//(step + 2) as usize * ppq / s.time_zoom as usize,
//);
//let phrase = &s.phrases[s.sequence.unwrap()];
//let (character, style) = match (
//phrase.contains_note_on(key, a, b),
//phrase.contains_note_on(key, b, c),
//) {
//(true, true) => ("█", wh),
//(true, false) => ("▀", wh),
//(false, true) => ("▄", wh),
//(false, false) => ("·", bw),
//};
//character.blit(buf, x + (5 + k - s.note_start) as u16, y, Some(style));
//}
//}
//if beat == step as usize {
//buf.set_string(x + 4, y, if beat % 2 == 0 { "▀" } else { "▄" }, Style::default().yellow());
//for key in s.note_start..s.note_start+area.width as usize {
//let _color = if s.notes_on[key as usize] {
//Style::default().red()
//} else {
//KEY_STYLE[key as usize % 12]
//};
//}
//}
//}
//}
//pub fn playhead (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) {
//let x = x + 5 + s.note_cursor as u16;
//let y = y + s.time_cursor as u16 / 2;
//let c = if s.time_cursor % 2 == 0 { "▀" } else { "▄" };
//buf.set_string(x, y, c, Style::default());
//}
//pub fn keys (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) {
//let ppq = s.timebase.ppq() as usize;
//let Rect { x, y, .. } = area;
//for key in s.note_start..s.note_start+area.width as usize {
//let x = x + (5 + key - s.note_start) as u16;
//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_STYLE[key as usize % 12];
//let mut is_on = s.notes_on[key as usize];
//let step = beat;
//let (a, b, c) = (
//(step + 0) as usize * ppq / s.time_zoom as usize,
//(step + 1) as usize * ppq / s.time_zoom as usize,
//(step + 2) as usize * ppq / s.time_zoom as usize,
//);
//let key = ::midly::num::u7::from(key as u8);
//let phrase = &s.phrases[s.sequence.unwrap()];
//is_on = is_on || phrase.contains_note_on(key, a, b);
//is_on = is_on || phrase.contains_note_on(key, b, c);
//if is_on {
//color = Style::default().red();
//}
//buf.set_string(x, y - 1, &format!("▄"), color);
//}
//}
////pub fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
////let Rect { x, y, width, height } = area;
////let header = draw_header(s, buf, area)?;
////let piano = match s.view {
////SequencerMode::Tiny => Rect { x, y, width, height: 0 },
////SequencerMode::Compact => Rect { x, y, width, height: 0 },
////SequencerMode::Vertical => self::vertical::draw(s, buf, Rect {
////x, y: y + header.height, width, height,
////})?,
////SequencerMode::Horizontal => self::horizontal::draw(
////buf,
////Rect { x, y: y + header.height, width, height, },
////s.phrase(),
////s.timebase.ppq() as usize,
////s.time_cursor,
////s.time_start,
////s.time_zoom,
////s.note_cursor,
////s.note_start,
////None
////)?,
////};
////Ok(draw_box(buf, Rect {
////x, y,
////width: header.width.max(piano.width),
////height: header.height + piano.height
////}))
////}
////pub fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
////let Rect { x, y, width, .. } = area;
////let style = Style::default().gray();
////crate::view::TransportView {
////timebase: &s.timebase,
////playing: s.playing,
////record: s.recording,
////overdub: s.overdub,
////monitor: s.monitoring,
////frame: 0
////}.render(buf, area)?;
////let separator = format!("├{}┤", "-".repeat((width - 2).into()));
////separator.blit(buf, x, y + 2, Some(style.dim()));
////let _ = draw_clips(s, buf, area)?;
////Ok(Rect { x, y, width, height: 3 })
////}
////pub fn draw_clips (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
////let Rect { x, y, .. } = area;
////let style = Style::default().gray();
////for (i, sequence) in s.phrases.iter().enumerate() {
////let label = format!("▶ {}", &sequence.name);
////label.blit(buf, x + 2, y + 3 + (i as u16)*2, Some(if Some(i) == s.sequence {
////match s.playing {
////TransportState::Rolling => style.white().bold(),
////_ => style.not_dim().bold()
////}
////} else {
////style.dim()
////}));
////}
////Ok(Rect { x, y, width: 14, height: 14 })
////}
//}