mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 04:36:45 +01:00
bye sequencer
This commit is contained in:
parent
2165e5d45d
commit
316fe45b2a
12 changed files with 510 additions and 759 deletions
|
|
@ -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 })
|
||||
////}
|
||||
//}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue