use crate::{core::*,model::*,view::*}; #[derive(Debug, Clone)] pub enum SequencerMode { Tiny, Compact, Horizontal, Vertical, } pub struct SequencerView<'a> { pub focused: bool, pub phrase: Option<&'a Phrase>, 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, /// Current time pub now: usize } impl<'a> Render for SequencerView<'a> { fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { let Rect { x, y, width, height } = area; let style = Some(Style::default().green().dim()); if self.focused { lozenge_left(buf, x, y, height, style); lozenge_right(buf, x + width - 1, y, height, style); } self.draw_horizontal(buf, area)?; Ok(area) } } impl<'a> SequencerView<'a> { fn draw_horizontal (&self, buf: &mut Buffer, area: Rect) -> Usually<()> { let style = Some(if self.focused { Style::default().green().not_dim() } else { Style::default().green().dim() }); let notes = &[]; pulse_to_note_length(self.time_zoom) .blit(buf, area.x, area.y, Some(Style::default().dim())); self::horizontal::keys(buf, area, self.note_start, notes)?; if let Some(phrase) = self.phrase { self::horizontal::timer(buf, area, self.time_start, self.time_zoom, self.now % phrase.length); self::horizontal::lanes(buf, area, phrase, self.ppq, self.time_zoom, self.time_start, self.note_start); } let style = style.unwrap_or_else(||{Style::default().green().not_dim()}); self::horizontal::cursor(buf, area, style, self.time_cursor, self.note_cursor); Ok(()) } } fn pulse_to_note_length (time_z: usize) -> &'static str { 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", _ => "" } } mod horizontal { use crate::core::*; use super::*; pub fn timer (buf: &mut Buffer, area: Rect, time0: usize, time_z: usize, now: usize) { let Rect { x, width, .. } = area; let offset = 5; for x in x+offset..x+width-offset { let step = (time0 + (x-offset) as usize) * time_z; let next_step = (time0 + (x-offset) as usize + 1) * time_z; let style = if step <= now && now < next_step { Style::default().yellow().bold().not_dim() } else { Style::default() }; "-".blit(buf, x, area.y, Some(style)) } } pub fn keys (buf: &mut Buffer, area: Rect, note0: usize, _notes: &[bool]) -> Usually { 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().not_dim()); let offset = 5; for x in x+offset..x+width-offset { let step = (time0 + (x-offset) as usize) * time_z; let next_step = (time0 + (x-offset) as usize + 1) * 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())); } 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, mut style) = match ( phrase.contains_note_on(u7::from_int_lossy(note_a as u8), step, next_step), phrase.contains_note_on(u7::from_int_lossy(note_b as u8), step, next_step), ) { (true, true) => ("█", wh), (false, true) => ("▀", wh), (true, false) => ("▄", wh), (false, false) => (if step % ppq == 0 { "|" } else { "·" }, bw), }; let y = y + height.saturating_sub(index+2) as u16; if step > phrase.length { style = Style::default().gray() } 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; //} //} //} //mod vertical { //use super::*; //pub fn draw ( //s: &Sequencer, //buf: &mut Buffer, //mut area: Rect, //) -> Usually { //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 { ////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 { ////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 { ////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 }) ////} //}