full block piano roll, pt.2

This commit is contained in:
🪞👃🪞 2024-12-04 00:34:40 +01:00
parent 64ac577f4a
commit 35c0470d15
3 changed files with 90 additions and 59 deletions

View file

@ -122,3 +122,9 @@ impl Command<PhraseEditorModel> for PhraseCommand {
})
}
}
#[derive(Copy, Clone, Debug)]
pub enum PhraseEditMode {
Note,
Scroll,
}

View file

@ -57,13 +57,16 @@ impl Default for PhraseEditorModel {
now: Pulse::default().into(),
size: Measure::new(),
edit_mode: PhraseEditMode::Scroll,
view_mode: PhraseViewMode::Horizontal,
note_lo: 0.into(),
note_point: 24.into(),
time_start: 0.into(),
time_point: 0.into(),
time_clamp: 0.into(),
time_scale: 24.into(),
view_mode: PhraseViewMode::PianoHorizontal {
time_zoom: 24,
note_zoom: PhraseViewNoteZoom::N(1)
},
}
}
}
@ -81,7 +84,7 @@ impl PhraseEditorModel {
let end = (start + self.note_len) % phrase.length;
phrase.notes[time].push(MidiMessage::NoteOn { key, vel });
phrase.notes[end].push(MidiMessage::NoteOff { key, vel });
self.buffer = Self::redraw(&phrase);
self.buffer = self.view_mode.draw(&phrase);
}
}
/// Move time cursor forward by current note length
@ -96,7 +99,7 @@ impl PhraseEditorModel {
if let Some(phrase) = phrase {
self.phrase = Some(phrase.clone());
self.time_clamp.store(phrase.read().unwrap().length, Ordering::Relaxed);
self.buffer = Self::redraw(&*phrase.read().unwrap());
self.buffer = self.view_mode.draw(&*phrase.read().unwrap());
} else {
self.phrase = None;
self.time_clamp.store(0, Ordering::Relaxed);
@ -105,19 +108,6 @@ impl PhraseEditorModel {
}
}
#[derive(Copy, Clone, Debug)]
pub enum PhraseEditMode {
Note,
Scroll,
}
#[derive(Copy, Clone, Debug)]
pub enum PhraseViewMode {
Horizontal,
HorizontalHalf,
Vertical,
}
pub trait HasEditor {
fn editor (&self) -> &PhraseEditorModel;
fn editor_focused (&self) -> bool;

View file

@ -1,14 +1,14 @@
use crate::*;
pub struct PhraseView<'a> {
focused: bool,
entered: bool,
phrase: &'a Option<Arc<RwLock<Phrase>>>,
buffer: &'a BigBuffer,
note_len: usize,
now: &'a Arc<Pulse>,
size: &'a Measure<Tui>,
focused: bool,
entered: bool,
phrase: &'a Option<Arc<RwLock<Phrase>>>,
buffer: &'a BigBuffer,
note_len: usize,
now: &'a Arc<Pulse>,
size: &'a Measure<Tui>,
view_mode: &'a PhraseViewMode,
note_point: usize,
note_range: (usize, usize),
@ -42,14 +42,14 @@ impl<'a, T: HasEditor> From<&'a T> for PhraseView<'a> {
}
Self {
focused: state.editor_focused(),
entered: state.editor_entered(),
note_len: editor.note_len,
phrase: &editor.phrase,
buffer: &editor.buffer,
now: &editor.now,
size: &editor.size,
focused: state.editor_focused(),
entered: state.editor_entered(),
note_len: editor.note_len,
phrase: &editor.phrase,
buffer: &editor.buffer,
now: &editor.now,
size: &editor.size,
view_mode: &editor.view_mode,
note_point,
note_range: (note_lo, note_hi),
@ -72,6 +72,7 @@ impl<'a> Content for PhraseView<'a> {
phrase,
size,
buffer,
view_mode,
note_len,
note_range: (note_lo, note_hi),
note_names: (note_lo_name, note_hi_name),
@ -122,18 +123,19 @@ impl<'a> Content for PhraseView<'a> {
size.set_wh(area.w(), h - 1);
Ok(if to.area().h() >= 2 {
let area = to.area();
to.buffer_update(area, &move |cell, x, y|{
cell.set_bg(notes_bg_null);
let src_x = (x as usize + time_start) * time_scale;
let src_y = y as usize + note_lo / 2;
if src_x < buffer.width && src_y < buffer.height - 1 {
buffer.get(src_x, (note_hi - y as usize) / 2).map(|src|{
cell.set_symbol(src.symbol());
cell.set_fg(src.fg);
cell.set_bg(src.bg);
});
}
});
view_mode.blit(buffer, &mut to.buffer, area, *time_start, *note_hi);
//to.buffer_update(area, &move |cell, x, y|{
//cell.set_bg(notes_bg_null);
//let src_x = (x as usize + time_start) * time_scale;
//let src_y = y as usize + note_lo / 2;
//if src_x < buffer.width && src_y < buffer.height - 1 {
//buffer.get(src_x, (note_hi - y as usize) / 2).map(|src|{
//cell.set_symbol(src.symbol());
//cell.set_fg(src.fg);
//cell.set_bg(src.bg);
//});
//}
//});
})
};
let cursor = move|to: &mut TuiOutput|Ok(if *focused && *entered {
@ -230,27 +232,53 @@ pub enum PhraseViewMode {
#[derive(Copy, Clone, Debug)]
pub enum PhraseViewNoteZoom {
Half,
N(usize),
Half,
Octant,
}
impl PhraseViewMode {
/// Return a new [BigBuffer] containing a render of the phrase.
fn draw (&self, phrase: &Phrase) -> BigBuffer {
pub fn draw (&self, phrase: &Phrase) -> BigBuffer {
let mut buffer = BigBuffer::new(self.buffer_width(phrase), self.buffer_height(phrase));
match self {
Self::PianoHorizontal { time_zoom, note_zoom } => match note_zoom {
PhraseViewNoteZoom::Half => Self::draw_piano_horizontal_half(
&mut buffer, phrase, *time_zoom
),
PhraseViewNoteZoom::N(_) => Self::draw_piano_horizontal(
&mut buffer, phrase, *time_zoom, 1
),
_ => unimplemented!(),
},
_ => unimplemented!(),
}
buffer
}
/// Draw a subsection of the [BigBuffer] onto a regular ratatui [Buffer].
fn blit (
&self,
source: &BigBuffer,
target: &mut Buffer,
area: impl Area<u16>,
time_start: usize,
note_hi: usize,
) {
match self {
Self::PianoHorizontal { .. } => {
let [x0, y0, w, h] = area.xywh();
for (x, target_x) in (x0..x0+w).enumerate() {
for (y, target_y) in (y0..y0+h).enumerate() {
let source_x = time_start + x;
let source_y = note_hi - y;
let target_cell = target.get_mut(target_x, target_y);
target_cell.set_char('x');
if let Some(source_cell) = source.get(source_x, source_y) {
*target_cell = source_cell.clone();
}
}
}
},
_ => unimplemented!()
}
}
/// Determine the required width to render the phrase.
fn buffer_width (&self, phrase: &Phrase) -> usize {
match self {
@ -260,6 +288,7 @@ impl PhraseViewMode {
Self::PianoVertical { note_zoom, .. } => match note_zoom {
PhraseViewNoteZoom::Half => 64,
PhraseViewNoteZoom::N(n) => 128*n,
_ => unimplemented!()
},
}
}
@ -269,19 +298,16 @@ impl PhraseViewMode {
Self::PianoHorizontal { note_zoom, .. } => match note_zoom {
PhraseViewNoteZoom::Half => 64,
PhraseViewNoteZoom::N(n) => 128*n,
_ => unimplemented!()
},
Self::PianoVertical { time_zoom, .. } => {
phrase.length / time_zoom
},
}
}
fn draw_piano_horizontal_half (
_: &mut BigBuffer, _: &Phrase, _: usize
) {
unimplemented!()
}
/// Draw the piano roll using full blocks on note on and half blocks on legato: █▄ █▄ █▄
fn draw_piano_horizontal (
buffer: &mut BigBuffer, phrase: &Phrase, time_zoom: usize, note_zoom: usize
buffer: &mut BigBuffer, phrase: &Phrase, time_zoom: usize, _: usize
) {
let mut notes_on = [false;128];
for col in 0..buffer.width {
@ -295,6 +321,8 @@ impl PhraseViewMode {
} else {
cell.set_char(' ');
}
cell.set_fg(Color::Rgb(255, 255, 255));
cell.set_bg(Color::Rgb(28, 35, 25));
}
for event in phrase.notes[time].iter() {
match event {
@ -311,10 +339,17 @@ impl PhraseViewMode {
}
}
}
//for row in 0..buf.height {
//let pitch = 127 - row;
//}
}
/// TODO: Draw the piano roll using octant blocks (U+1CD00-U+1CDE5)
fn draw_piano_horizontal_octant (
_: &mut BigBuffer, _: &Phrase, _: usize
) {
unimplemented!()
}
/// TODO: Draw the piano roll using half blocks: ▄▀▄
fn draw_piano_horizontal_half (
_: &mut BigBuffer, _: &Phrase, _: usize
) {
unimplemented!()
}
}