tek/src/model/sequencer.rs

197 lines
5.7 KiB
Rust

use crate::{core::*, model::{FixedAxis, ScaledAxis, Phrase}};
pub struct Sequencer {
pub mode: bool,
pub focused: bool,
pub entered: bool,
pub phrase: Option<Arc<RwLock<Phrase>>>,
pub buffer: Buffer,
pub keys: Buffer,
/// Highlight input keys
pub keys_in: [bool; 128],
/// Highlight output keys
pub keys_out: [bool; 128],
pub now: usize,
pub ppq: usize,
pub note_axis: FixedAxis<u16>,
pub time_axis: ScaledAxis<u16>,
}
impl Sequencer {
pub fn new () -> Self {
Self {
buffer: Buffer::empty(Rect::default()),
keys: keys_vert(),
entered: false,
focused: false,
mode: false,
keys_in: [false;128],
keys_out: [false;128],
phrase: None,
now: 0,
ppq: 96,
note_axis: FixedAxis {
start: 12,
point: Some(36)
},
time_axis: ScaledAxis {
start: 0,
scale: 24,
point: Some(0)
},
}
}
/// Select which pattern to display. This pre-renders it to the buffer at full resolution.
/// FIXME: Support phrases longer that 65536 ticks
pub fn show (&mut self, phrase: Option<&Arc<RwLock<Phrase>>>) -> Usually<()> {
self.phrase = phrase.map(Clone::clone);
if let Some(ref phrase) = self.phrase {
let width = u16::MAX.min(phrase.read().unwrap().length as u16);
let mut buffer = Buffer::empty(Rect { x: 0, y: 0, width, height: 64 });
let phrase = phrase.read().unwrap();
fill_seq_bg(&mut buffer, phrase.length, self.ppq)?;
fill_seq_fg(&mut buffer, &phrase)?;
self.buffer = buffer;
} else {
self.buffer = Buffer::empty(Rect::default())
}
Ok(())
}
}
fn keys_vert () -> Buffer {
let area = Rect { x: 0, y: 0, width: 5, height: 64 };
let mut buffer = Buffer::empty(area);
buffer_update(&mut buffer, area, &|cell, x, y| {
let y = 63 - y;
match x {
0 => {
cell.set_char('▀');
let (fg, bg) = key_colors(6 - y % 6);
cell.set_fg(fg);
cell.set_bg(bg);
},
1 => {
cell.set_char('▀');
cell.set_fg(Color::White);
cell.set_bg(Color::White);
},
2 => if y % 6 == 0 {
cell.set_char('C');
},
3 => if y % 6 == 0 {
cell.set_symbol(nth_octave(y / 6));
},
_ => {}
}
});
buffer
}
fn nth_octave (index: u16) -> &'static str {
match index {
0 => "-1",
1 => "0",
2 => "1",
3 => "2",
4 => "3",
5 => "4",
6 => "5",
7 => "6",
8 => "7",
9 => "8",
10 => "9",
_ => unreachable!()
}
}
fn key_colors (index: u16) -> (Color, Color) {
match index % 6 {
0 => (Color::White, Color::Black),
1 => (Color::White, Color::Black),
2 => (Color::White, Color::White),
3 => (Color::Black, Color::White),
4 => (Color::Black, Color::White),
5 => (Color::Black, Color::White),
_ => unreachable!()
}
}
fn fill_seq_bg (buf: &mut Buffer, length: usize, ppq: usize) -> Usually<()> {
for x in 0 .. buf.area.width - buf.area.x {
if x as usize >= length {
break
}
let style = Style::default();
let cell = buf.get_mut(x, buf.area.y);
cell.set_char('-');
cell.set_style(style);
for y in 0 .. buf.area.height - buf.area.y {
let cell = buf.get_mut(x, y);
cell.set_char(char_seq_bg(ppq, x));
cell.set_fg(Color::Gray);
cell.modifier = Modifier::DIM;
}
}
Ok(())
}
fn char_seq_bg (ppq: usize, x: u16) -> char {
if ppq == 0 {
'·'
} else if x % (4 * ppq as u16) == 0 {
'│'
} else if x % ppq as u16 == 0 {
'╎'
} else {
'·'
}
}
fn fill_seq_fg (buf: &mut Buffer, phrase: &Phrase) -> Usually<()> {
let mut notes_on = [false;128];
for x in 0 .. buf.area.width - buf.area.x {
if x as usize >= phrase.length {
break
}
if let Some(notes) = phrase.notes.get(x as usize) {
for note in notes {
if phrase.percussive {
match note {
MidiMessage::NoteOn { key, .. } =>
notes_on[key.as_int() as usize] = true,
_ => {}
}
} else {
match note {
MidiMessage::NoteOn { key, .. } =>
notes_on[key.as_int() as usize] = true,
MidiMessage::NoteOff { key, .. } =>
notes_on[key.as_int() as usize] = false,
_ => {}
}
}
}
for y in 0 .. (buf.area.height - buf.area.y) / 2 {
if y >= 64 {
break
}
if let Some(block) = half_block(
notes_on[y as usize * 2],
notes_on[y as usize * 2 + 1],
) {
let cell = buf.get_mut(x, y);
cell.set_char(block);
cell.set_fg(Color::White);
}
}
if phrase.percussive {
notes_on.fill(false);
}
}
}
Ok(())
}