mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
fix range of piano roll
This commit is contained in:
parent
1c93646fcf
commit
c1e6a0137e
3 changed files with 83 additions and 90 deletions
|
|
@ -6,7 +6,7 @@ pub trait MidiViewport<E: Engine>: MidiRange + MidiPoint + HasSize<E> {
|
||||||
fn autoscroll (&self) {
|
fn autoscroll (&self) {
|
||||||
let note_lo = self.note_lo();
|
let note_lo = self.note_lo();
|
||||||
let note_axis = self.note_axis();
|
let note_axis = self.note_axis();
|
||||||
let note_hi = self.note_hi().saturating_sub(1);
|
let note_hi = self.note_hi();
|
||||||
let note_point = self.note_point().min(127);
|
let note_point = self.note_point().min(127);
|
||||||
if note_point < note_lo {
|
if note_point < note_lo {
|
||||||
self.set_note_lo(note_point);
|
self.set_note_lo(note_point);
|
||||||
|
|
@ -57,7 +57,7 @@ pub trait MidiRange {
|
||||||
fn set_note_lo (&self, x: usize);
|
fn set_note_lo (&self, x: usize);
|
||||||
fn note_axis (&self) -> usize;
|
fn note_axis (&self) -> usize;
|
||||||
fn time_axis (&self) -> usize;
|
fn time_axis (&self) -> usize;
|
||||||
fn note_hi (&self) -> usize { self.note_lo() + self.note_axis() }
|
fn note_hi (&self) -> usize { (self.note_lo() + self.note_axis().saturating_sub(1)).min(127) }
|
||||||
fn time_end (&self) -> usize { self.time_start() + self.time_axis() * self.time_zoom() }
|
fn time_end (&self) -> usize { self.time_start() + self.time_axis() * self.time_zoom() }
|
||||||
}
|
}
|
||||||
impl MidiRange for MidiRangeModel {
|
impl MidiRange for MidiRangeModel {
|
||||||
|
|
@ -67,8 +67,8 @@ impl MidiRange for MidiRangeModel {
|
||||||
fn set_time_lock (&self, x: bool) { self.time_lock.store(x, Relaxed); }
|
fn set_time_lock (&self, x: bool) { self.time_lock.store(x, Relaxed); }
|
||||||
fn time_start (&self) -> usize { self.time_start.load(Relaxed) }
|
fn time_start (&self) -> usize { self.time_start.load(Relaxed) }
|
||||||
fn set_time_start (&self, x: usize) { self.time_start.store(x, Relaxed); }
|
fn set_time_start (&self, x: usize) { self.time_start.store(x, Relaxed); }
|
||||||
fn note_lo (&self) -> usize { self.note_lo.load(Relaxed) }
|
fn note_lo (&self) -> usize { self.note_lo.load(Relaxed).min(127) }
|
||||||
fn set_note_lo (&self, x: usize) { self.note_lo.store(x, Relaxed); }
|
fn set_note_lo (&self, x: usize) { self.note_lo.store(x.min(127), Relaxed); }
|
||||||
fn note_axis (&self) -> usize { self.note_axis.load(Relaxed) }
|
fn note_axis (&self) -> usize { self.note_axis.load(Relaxed) }
|
||||||
fn time_axis (&self) -> usize { self.time_axis.load(Relaxed) }
|
fn time_axis (&self) -> usize { self.time_axis.load(Relaxed) }
|
||||||
}
|
}
|
||||||
|
|
@ -103,8 +103,8 @@ pub trait MidiPoint {
|
||||||
impl MidiPoint for MidiPointModel {
|
impl MidiPoint for MidiPointModel {
|
||||||
fn note_len (&self) -> usize { self.note_len.load(Relaxed)}
|
fn note_len (&self) -> usize { self.note_len.load(Relaxed)}
|
||||||
fn set_note_len (&self, x: usize) { self.note_len.store(x, Relaxed) }
|
fn set_note_len (&self, x: usize) { self.note_len.store(x, Relaxed) }
|
||||||
fn note_point (&self) -> usize { self.note_point.load(Relaxed) }
|
fn note_point (&self) -> usize { self.note_point.load(Relaxed).min(127) }
|
||||||
fn set_note_point (&self, x: usize) { self.note_point.store(x, Relaxed) }
|
fn set_note_point (&self, x: usize) { self.note_point.store(x.min(127), Relaxed) }
|
||||||
fn time_point (&self) -> usize { self.time_point.load(Relaxed) }
|
fn time_point (&self) -> usize { self.time_point.load(Relaxed) }
|
||||||
fn set_time_point (&self, x: usize) { self.time_point.store(x, Relaxed) }
|
fn set_time_point (&self, x: usize) { self.time_point.store(x, Relaxed) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ impl Command<PhraseEditorModel> for PhraseCommand {
|
||||||
SetTimeZoom(x) => { state.set_time_zoom(x); },
|
SetTimeZoom(x) => { state.set_time_zoom(x); },
|
||||||
SetTimeLock(x) => { state.set_time_lock(x); },
|
SetTimeLock(x) => { state.set_time_lock(x); },
|
||||||
SetTimeScroll(x) => { state.set_time_start(x); },
|
SetTimeScroll(x) => { state.set_time_start(x); },
|
||||||
SetNoteScroll(x) => { state.set_note_lo(x); },
|
SetNoteScroll(x) => { state.set_note_lo(x.min(127)); },
|
||||||
SetNoteLength(x) => { state.set_note_len(x); },
|
SetNoteLength(x) => { state.set_note_len(x); },
|
||||||
SetTimeCursor(x) => {
|
SetTimeCursor(x) => {
|
||||||
state.set_time_point(x);
|
state.set_time_point(x);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,28 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
fn note_y_iter (note_lo: usize, note_hi: usize, y0: u16) -> impl Iterator<Item=(usize, u16, usize)> {
|
||||||
|
(note_lo..=note_hi).rev().enumerate().map(move|(y, n)|(y, y0 + y as u16, n))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_key (note: usize) -> &'static str {
|
||||||
|
match note % 12 {
|
||||||
|
11 => "████▌",
|
||||||
|
10 => " ",
|
||||||
|
9 => "████▌",
|
||||||
|
8 => " ",
|
||||||
|
7 => "████▌",
|
||||||
|
6 => " ",
|
||||||
|
5 => "████▌",
|
||||||
|
4 => "████▌",
|
||||||
|
3 => " ",
|
||||||
|
2 => "████▌",
|
||||||
|
1 => " ",
|
||||||
|
0 => "████▌",
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A phrase, rendered as a horizontal piano roll.
|
/// A phrase, rendered as a horizontal piano roll.
|
||||||
pub struct PianoHorizontal {
|
pub struct PianoHorizontal {
|
||||||
phrase: Option<Arc<RwLock<Phrase>>>,
|
phrase: Option<Arc<RwLock<Phrase>>>,
|
||||||
|
|
@ -39,16 +61,13 @@ impl PianoHorizontal {
|
||||||
|
|
||||||
render!(|self: PianoHorizontal|{
|
render!(|self: PianoHorizontal|{
|
||||||
let color = self.color;
|
let color = self.color;
|
||||||
let range = &self.range;
|
let keys = move||PianoHorizontalKeys(&self);
|
||||||
let point = &self.point;
|
let timeline = move||PianoHorizontalTimeline(&self);
|
||||||
let buffer = &self.buffer;
|
let notes = move||PianoHorizontalNotes(&self);
|
||||||
let keys = move||PianoHorizontalKeys { range, point, color }; //note_lo, note_hi, note_point: Some(note_point), };
|
let cursor = move||PianoHorizontalCursor(&self);
|
||||||
let timeline = move||PianoHorizontalTimeline { range, color };
|
|
||||||
let notes = move||PianoHorizontalNotes { range, buffer };
|
|
||||||
let cursor = move||PianoHorizontalCursor { range, point };
|
|
||||||
let keys_width = 5;
|
let keys_width = 5;
|
||||||
Tui::fill_xy(Tui::bg(color.darker.rgb, Bsp::s(
|
Tui::fill_xy(Tui::bg(color.darker.rgb, Bsp::s(
|
||||||
Tui::fill_x(Tui::push_x(keys_width, timeline())),
|
Tui::fixed_y(1, Tui::fill_x(Tui::push_x(keys_width, timeline()))),
|
||||||
Bsp::w(
|
Bsp::w(
|
||||||
Tui::shrink_x(keys_width, Tui::fill_xy(
|
Tui::shrink_x(keys_width, Tui::fill_xy(
|
||||||
lay!([&self.size, Tui::fill_xy(lay!([
|
lay!([&self.size, Tui::fill_xy(lay!([
|
||||||
|
|
@ -61,55 +80,34 @@ render!(|self: PianoHorizontal|{
|
||||||
)))
|
)))
|
||||||
});
|
});
|
||||||
|
|
||||||
pub struct PianoHorizontalTimeline<'a> {
|
pub struct PianoHorizontalTimeline<'a>(&'a PianoHorizontal);
|
||||||
color: ItemPalette,
|
|
||||||
range: &'a MidiRangeModel,
|
|
||||||
}
|
|
||||||
render!(|self: PianoHorizontalTimeline<'a>|Tui::fixed_y(1, Tui::fg_bg(
|
render!(|self: PianoHorizontalTimeline<'a>|Tui::fixed_y(1, Tui::fg_bg(
|
||||||
self.color.lightest.rgb,
|
self.0.color.lightest.rgb,
|
||||||
self.color.darkest.rgb,
|
self.0.color.darkest.rgb,
|
||||||
format!("{}*{}", self.range.time_start(), self.range.time_zoom()).as_str()
|
format!("{}*{}", self.0.time_start(), self.0.time_zoom()).as_str()
|
||||||
)));
|
)));
|
||||||
|
|
||||||
pub struct PianoHorizontalKeys<'a> {
|
pub struct PianoHorizontalKeys<'a>(&'a PianoHorizontal);
|
||||||
color: ItemPalette,
|
|
||||||
range: &'a MidiRangeModel,
|
|
||||||
point: &'a MidiPointModel,
|
|
||||||
}
|
|
||||||
render!(|self: PianoHorizontalKeys<'a>|render(|to|Ok({
|
render!(|self: PianoHorizontalKeys<'a>|render(|to|Ok({
|
||||||
let color = self.color;
|
let color = self.0.color;
|
||||||
let note_lo = self.range.note_lo();
|
let note_lo = self.0.note_lo();
|
||||||
let note_hi = self.range.note_hi();
|
let note_hi = self.0.note_hi();
|
||||||
let note_point = self.point.note_point();
|
let note_point = self.0.note_point();
|
||||||
let [x, y0, w, h] = to.area().xywh();
|
let [x, y0, w, h] = to.area().xywh();
|
||||||
let key_style = Some(Style::default().fg(Color::Rgb(192, 192, 192)).bg(Color::Rgb(0, 0, 0)));
|
let key_style = Some(Style::default().fg(Color::Rgb(192, 192, 192)).bg(Color::Rgb(0, 0, 0)));
|
||||||
let off_style = Some(Style::default().fg(TuiTheme::g(160)));
|
let off_style = Some(Style::default().fg(TuiTheme::g(160)));
|
||||||
let on_style = Some(Style::default().fg(TuiTheme::g(255)).bg(self.color.light.rgb).bold());
|
let on_style = Some(Style::default().fg(TuiTheme::g(255)).bg(color.light.rgb).bold());
|
||||||
for (y, note) in (note_lo..note_hi).rev().enumerate().map(|(y, n)|(y0 + y as u16, n)) {
|
for (area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
||||||
let key = match note % 12 {
|
to.blit(&to_key(note), x, screen_y, key_style);
|
||||||
11 => "████▌",
|
if note > 127 {
|
||||||
10 => " ",
|
continue
|
||||||
9 => "████▌",
|
}
|
||||||
8 => " ",
|
|
||||||
7 => "████▌",
|
|
||||||
6 => " ",
|
|
||||||
5 => "████▌",
|
|
||||||
4 => "████▌",
|
|
||||||
3 => " ",
|
|
||||||
2 => "████▌",
|
|
||||||
1 => " ",
|
|
||||||
0 => "████▌",
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
to.blit(&key, x, y, key_style);
|
|
||||||
|
|
||||||
if note == note_point {
|
if note == note_point {
|
||||||
to.blit(&format!("{:<5}", to_note_name(note)), x, y, on_style)
|
to.blit(&format!("{:<5}", to_note_name(note)), x, screen_y, on_style)
|
||||||
} else {
|
} else {
|
||||||
to.blit(&to_note_name(note), x, y, off_style)
|
to.blit(&to_note_name(note), x, screen_y, off_style)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let debug = false;
|
let debug = false;
|
||||||
if debug {
|
if debug {
|
||||||
to.blit(&format!("XYU"), x, y0, None);
|
to.blit(&format!("XYU"), x, y0, None);
|
||||||
|
|
@ -122,31 +120,28 @@ render!(|self: PianoHorizontalKeys<'a>|render(|to|Ok({
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
|
|
||||||
pub struct PianoHorizontalCursor<'a> {
|
pub struct PianoHorizontalCursor<'a>(&'a PianoHorizontal);
|
||||||
range: &'a MidiRangeModel,
|
|
||||||
point: &'a MidiPointModel,
|
|
||||||
}
|
|
||||||
render!(|self: PianoHorizontalCursor<'a>|render(|to|Ok({
|
render!(|self: PianoHorizontalCursor<'a>|render(|to|Ok({
|
||||||
|
let note_hi = self.0.note_hi();
|
||||||
|
let note_len = self.0.note_len();
|
||||||
|
let note_lo = self.0.note_lo();
|
||||||
|
let note_point = self.0.note_point();
|
||||||
|
let time_point = self.0.time_point();
|
||||||
|
let time_start = self.0.time_start();
|
||||||
|
let time_zoom = self.0.time_zoom();
|
||||||
let [x0, y0, w, _] = to.area().xywh();
|
let [x0, y0, w, _] = to.area().xywh();
|
||||||
let note_hi = self.range.note_hi();
|
|
||||||
let note_len = self.point.note_len();
|
|
||||||
let note_lo = self.range.note_lo();
|
|
||||||
let note_point = self.point.note_point();
|
|
||||||
let time_point = self.point.time_point();
|
|
||||||
let time_start = self.range.time_start();
|
|
||||||
let time_zoom = self.range.time_zoom();
|
|
||||||
let style = Some(Style::default().fg(Color::Rgb(0,255,0)));
|
let style = Some(Style::default().fg(Color::Rgb(0,255,0)));
|
||||||
for (input_y, output_y, note) in (note_lo..note_hi).rev().enumerate().map(|(y, n)|(y, y0 + y as u16, n)) {
|
for (area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
||||||
if note == note_point {
|
if note == note_point {
|
||||||
for x in 0..w {
|
for x in 0..w {
|
||||||
let output_x = x0 + x as u16;
|
let screen_x = x0 + x as u16;
|
||||||
let time_1 = time_start + x as usize * time_zoom;
|
let time_1 = time_start + x as usize * time_zoom;
|
||||||
let time_2 = time_1 + time_zoom;
|
let time_2 = time_1 + time_zoom;
|
||||||
if time_1 <= time_point && time_point < time_2 {
|
if time_1 <= time_point && time_point < time_2 {
|
||||||
to.blit(&"█", output_x, output_y, style);
|
to.blit(&"█", screen_x, screen_y, style);
|
||||||
let tail = note_len as u16 / time_zoom as u16;
|
let tail = note_len as u16 / time_zoom as u16;
|
||||||
for x_tail in (output_x + 1)..(output_x + tail) {
|
for x_tail in (screen_x + 1)..(screen_x + tail) {
|
||||||
to.blit(&"▂", x_tail, output_y, style);
|
to.blit(&"▂", x_tail, screen_y, style);
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -156,36 +151,34 @@ render!(|self: PianoHorizontalCursor<'a>|render(|to|Ok({
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
|
|
||||||
pub struct PianoHorizontalNotes<'a> {
|
pub struct PianoHorizontalNotes<'a>(&'a PianoHorizontal);
|
||||||
range: &'a MidiRangeModel,
|
|
||||||
buffer: &'a BigBuffer,
|
|
||||||
}
|
|
||||||
render!(|self: PianoHorizontalNotes<'a>|render(|to|Ok({
|
render!(|self: PianoHorizontalNotes<'a>|render(|to|Ok({
|
||||||
let note_hi = self.range.note_hi();
|
let time_start = self.0.time_start();
|
||||||
let note_lo = self.range.note_lo();
|
let note_hi = self.0.note_hi();
|
||||||
let time_start = self.range.time_start();
|
let note_lo = self.0.note_lo();
|
||||||
let source = &self.buffer;
|
let note_point = self.0.note_point();
|
||||||
|
let source = &self.0.buffer;
|
||||||
let [x0, y0, w, h] = to.area().xywh();
|
let [x0, y0, w, h] = to.area().xywh();
|
||||||
if h as usize != self.range.note_axis() {
|
if h as usize != self.0.note_axis() {
|
||||||
panic!("area height mismatch");
|
panic!("area height mismatch");
|
||||||
}
|
}
|
||||||
for (input_x, output_x) in (x0..x0+w).enumerate() {
|
for (area_x, screen_x) in (x0..x0+w).enumerate() {
|
||||||
for (input_y, output_y, note) in (note_lo..note_hi).rev().enumerate().map(|(y, n)|(y, y0 + y as u16, n)) {
|
for (area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
||||||
if input_x % 10 == 0 {
|
if area_x % 10 == 0 {
|
||||||
to.blit(&format!("{input_y} {note}"), output_x, output_y, None);
|
to.blit(&format!("{area_y} {note}"), screen_x, screen_y, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//for (input_y, output_y) in (y0..=y0+h).enumerate() {
|
//for (area_y, screen_y) in (y0..=y0+h).enumerate() {
|
||||||
//if note_hi < input_y {
|
//if note_hi < area_y {
|
||||||
//continue
|
//continue
|
||||||
//}
|
//}
|
||||||
//if note_hi > 127 {
|
//if note_hi > 127 {
|
||||||
//continue
|
//continue
|
||||||
//}
|
//}
|
||||||
//let note = note_hi - input_y;
|
//let note = note_hi - area_y;
|
||||||
//if input_x % 4 == 0 {
|
//if area_x % 4 == 0 {
|
||||||
//to.blit(&format!("{note}"), output_x, output_y, None);
|
//to.blit(&format!("{note}"), screen_x, screen_y, None);
|
||||||
//}
|
//}
|
||||||
////if y >= note_hi {
|
////if y >= note_hi {
|
||||||
////break
|
////break
|
||||||
|
|
@ -198,7 +191,7 @@ render!(|self: PianoHorizontalNotes<'a>|render(|to|Ok({
|
||||||
////let in_x = source_x < source.width;
|
////let in_x = source_x < source.width;
|
||||||
////let in_y = source_y < source.height;
|
////let in_y = source_y < source.height;
|
||||||
////if in_x && in_y {
|
////if in_x && in_y {
|
||||||
////let output_cell = to.buffer.get_mut(output_x, output_y);
|
////let output_cell = to.buffer.get_mut(screen_x, screen_y);
|
||||||
////if let Some(source_cell) = source.get(source_x, source_y) {
|
////if let Some(source_cell) = source.get(source_x, source_y) {
|
||||||
//*output_cell = source_cell.clone();
|
//*output_cell = source_cell.clone();
|
||||||
////}
|
////}
|
||||||
|
|
@ -213,7 +206,8 @@ render!(|self: PianoHorizontalNotes<'a>|render(|to|Ok({
|
||||||
to.blit(&format!("y0={y0} "), x0, y0+2, None);
|
to.blit(&format!("y0={y0} "), x0, y0+2, None);
|
||||||
to.blit(&format!("note_lo={note_lo}, {} ", to_note_name(note_lo)), x0, y0+3, None);
|
to.blit(&format!("note_lo={note_lo}, {} ", to_note_name(note_lo)), x0, y0+3, None);
|
||||||
to.blit(&format!("note_hi={note_hi}, {} ", to_note_name(note_hi)), x0, y0+4, None);
|
to.blit(&format!("note_hi={note_hi}, {} ", to_note_name(note_hi)), x0, y0+4, None);
|
||||||
to.blit(&format!("time_start={time_start} "), x0, y0+5, None);
|
to.blit(&format!("note_point={note_point}, {} ", to_note_name(note_point)), x0, y0+5, None);
|
||||||
|
to.blit(&format!("time_start={time_start} "), x0, y0+6, None);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
|
|
@ -293,7 +287,6 @@ impl MidiRange for PianoHorizontal {
|
||||||
fn note_lo (&self) -> usize { self.range.note_lo() }
|
fn note_lo (&self) -> usize { self.range.note_lo() }
|
||||||
fn note_axis (&self) -> usize { self.range.note_axis() }
|
fn note_axis (&self) -> usize { self.range.note_axis() }
|
||||||
fn time_axis (&self) -> usize { self.range.time_axis() }
|
fn time_axis (&self) -> usize { self.range.time_axis() }
|
||||||
fn note_hi (&self) -> usize { self.note_lo() + self.note_axis() }
|
|
||||||
}
|
}
|
||||||
impl MidiPoint for PianoHorizontal {
|
impl MidiPoint for PianoHorizontal {
|
||||||
fn note_len (&self) -> usize { self.point.note_len()}
|
fn note_len (&self) -> usize { self.point.note_len()}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue