mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 20:26:42 +01:00
wip: enabling standalone arranger
This commit is contained in:
parent
b6da43e93e
commit
7685072e4c
16 changed files with 445 additions and 370 deletions
|
|
@ -22,17 +22,6 @@ pub struct Sequencer {
|
|||
pub time_axis: ScaledAxis<usize>,
|
||||
}
|
||||
|
||||
render!(Sequencer |self, buf, area| {
|
||||
fill_bg(buf, area, Nord::bg_lo(self.focused, self.entered));
|
||||
self.horizontal_draw(buf, area)?;
|
||||
if self.focused && self.entered {
|
||||
Corners(Style::default().green().not_dim()).draw(buf, area)?;
|
||||
}
|
||||
Ok(area)
|
||||
});
|
||||
|
||||
handle!(Sequencer |self, e| handle_keymap(self, e, KEYMAP_SEQUENCER));
|
||||
|
||||
impl Sequencer {
|
||||
pub fn new () -> Self {
|
||||
Self {
|
||||
|
|
@ -59,279 +48,4 @@ impl Sequencer {
|
|||
},
|
||||
}
|
||||
}
|
||||
/// 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 = usize::MAX.min(phrase.read().unwrap().length);
|
||||
let mut buffer = BigBuffer::new(width, 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 = Default::default();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn style_focus (&self) -> Option<Style> {
|
||||
Some(if self.focused {
|
||||
Style::default().green().not_dim()
|
||||
} else {
|
||||
Style::default().green().dim()
|
||||
})
|
||||
}
|
||||
|
||||
fn style_timer_step (now: usize, step: usize, next_step: usize) -> Style {
|
||||
if step <= now && now < next_step {
|
||||
Style::default().yellow().bold().not_dim()
|
||||
} else {
|
||||
Style::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn index_to_color (&self, index: u16, default: Color) -> Color {
|
||||
let index = index as usize;
|
||||
if self.keys_in[index] && self.keys_out[index] {
|
||||
Color::Yellow
|
||||
} else if self.keys_in[index] {
|
||||
Color::Red
|
||||
} else if self.keys_out[index] {
|
||||
Color::Green
|
||||
} else {
|
||||
default
|
||||
}
|
||||
}
|
||||
|
||||
const H_KEYS_OFFSET: usize = 5;
|
||||
|
||||
fn horizontal_draw (&self, buf: &mut Buffer, area: Rect) -> Usually<()> {
|
||||
self.horizontal_keys(buf, area)?;
|
||||
if let Some(ref phrase) = self.phrase {
|
||||
self.horizontal_timer(buf, area, phrase)?;
|
||||
}
|
||||
self.horizontal_notes(buf, area)?;
|
||||
self.horizontal_cursor(buf, area)?;
|
||||
self.horizontal_quant(buf, area)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn horizontal_notes (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
if area.height < 2 {
|
||||
return Ok(area)
|
||||
}
|
||||
let area = Rect {
|
||||
x: area.x + Self::H_KEYS_OFFSET as u16,
|
||||
y: area.y + 1,
|
||||
width: area.width - Self::H_KEYS_OFFSET as u16,
|
||||
height: area.height - 2
|
||||
};
|
||||
buffer_update(buf, area, &move |cell, x, y|{
|
||||
let src_x = ((x as usize + self.time_axis.start) * self.time_axis.scale) as usize;
|
||||
let src_y = (y as usize + self.note_axis.start) as usize;
|
||||
if src_x < self.buffer.width && src_y < self.buffer.height - 1 {
|
||||
let src = self.buffer.get(src_x, self.buffer.height - src_y);
|
||||
src.map(|src|{
|
||||
cell.set_symbol(src.symbol());
|
||||
cell.set_fg(src.fg);
|
||||
});
|
||||
}
|
||||
});
|
||||
Ok(area)
|
||||
}
|
||||
|
||||
fn horizontal_keys (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
if area.height < 2 {
|
||||
return Ok(area)
|
||||
}
|
||||
let area = Rect {
|
||||
x: area.x,
|
||||
y: area.y + 1,
|
||||
width: 5,
|
||||
height: area.height - 2
|
||||
};
|
||||
buffer_update(buf, area, &|cell, x, y|{
|
||||
let y = y + self.note_axis.start as u16;
|
||||
if x < self.keys.area.width && y < self.keys.area.height {
|
||||
*cell = self.keys.get(x, y).clone()
|
||||
}
|
||||
});
|
||||
Ok(area)
|
||||
}
|
||||
|
||||
fn horizontal_quant (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let quant = ppq_to_name(self.time_axis.scale);
|
||||
let quant_x = area.x + area.width - 1 - quant.len() as u16;
|
||||
let quant_y = area.y + area.height - 2;
|
||||
quant.blit(buf, quant_x, quant_y, self.style_focus())
|
||||
}
|
||||
|
||||
fn horizontal_cursor (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
if let (Some(time), Some(note)) = (self.time_axis.point, self.note_axis.point) {
|
||||
let x = area.x + Self::H_KEYS_OFFSET as u16 + time as u16;
|
||||
let y = area.y + 1 + note as u16 / 2;
|
||||
let c = if note % 2 == 0 { "▀" } else { "▄" };
|
||||
c.blit(buf, x, y, self.style_focus())
|
||||
} else {
|
||||
Ok(Rect::default())
|
||||
}
|
||||
}
|
||||
|
||||
fn horizontal_timer (
|
||||
&self, buf: &mut Buffer, area: Rect, phrase: &RwLock<Phrase>
|
||||
) -> Usually<Rect> {
|
||||
let phrase = phrase.read().unwrap();
|
||||
let (time0, time_z, now) = (self.time_axis.start, self.time_axis.scale, self.now % phrase.length);
|
||||
let Rect { x, width, .. } = area;
|
||||
let x2 = x as usize + Self::H_KEYS_OFFSET;
|
||||
let x3 = x as usize + width as usize;
|
||||
for x in x2..x3 {
|
||||
let step = (time0 + x2) * time_z;
|
||||
let next_step = (time0 + x2 + 1) * time_z;
|
||||
let style = Self::style_timer_step(now, step as usize, next_step as usize);
|
||||
"-".blit(buf, x as u16, area.y, Some(style))?;
|
||||
}
|
||||
return Ok(Rect { x: area.x, y: area.y, width: area.width, height: 1 })
|
||||
}
|
||||
}
|
||||
|
||||
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 BigBuffer, length: usize, ppq: usize) -> Usually<()> {
|
||||
for x in 0..buf.width {
|
||||
if x as usize >= length {
|
||||
break
|
||||
}
|
||||
let style = Style::default();
|
||||
buf.get_mut(x, 0).map(|cell|{
|
||||
cell.set_char('-');
|
||||
cell.set_style(style);
|
||||
});
|
||||
for y in 0 .. buf.height {
|
||||
buf.get_mut(x, y).map(|cell|{
|
||||
cell.set_char(char_seq_bg(ppq, x as u16));
|
||||
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 BigBuffer, phrase: &Phrase) -> Usually<()> {
|
||||
let mut notes_on = [false;128];
|
||||
for x in 0..buf.width {
|
||||
if x as usize >= phrase.length {
|
||||
break
|
||||
}
|
||||
if let Some(notes) = phrase.notes.get(x as usize) {
|
||||
if phrase.percussive {
|
||||
for note in notes {
|
||||
match note {
|
||||
MidiMessage::NoteOn { key, .. } =>
|
||||
notes_on[key.as_int() as usize] = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for note in notes {
|
||||
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.height/2 {
|
||||
if y >= 64 {
|
||||
break
|
||||
}
|
||||
if let Some(block) = half_block(
|
||||
notes_on[y as usize * 2],
|
||||
notes_on[y as usize * 2 + 1],
|
||||
) {
|
||||
buf.get_mut(x, y).map(|cell|{
|
||||
cell.set_char(block);
|
||||
cell.set_fg(Color::White);
|
||||
});
|
||||
}
|
||||
}
|
||||
if phrase.percussive {
|
||||
notes_on.fill(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue