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(), now: Pulse::default().into(),
size: Measure::new(), size: Measure::new(),
edit_mode: PhraseEditMode::Scroll, edit_mode: PhraseEditMode::Scroll,
view_mode: PhraseViewMode::Horizontal,
note_lo: 0.into(), note_lo: 0.into(),
note_point: 24.into(), note_point: 24.into(),
time_start: 0.into(), time_start: 0.into(),
time_point: 0.into(), time_point: 0.into(),
time_clamp: 0.into(), time_clamp: 0.into(),
time_scale: 24.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; let end = (start + self.note_len) % phrase.length;
phrase.notes[time].push(MidiMessage::NoteOn { key, vel }); phrase.notes[time].push(MidiMessage::NoteOn { key, vel });
phrase.notes[end].push(MidiMessage::NoteOff { 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 /// Move time cursor forward by current note length
@ -96,7 +99,7 @@ impl PhraseEditorModel {
if let Some(phrase) = phrase { if let Some(phrase) = phrase {
self.phrase = Some(phrase.clone()); self.phrase = Some(phrase.clone());
self.time_clamp.store(phrase.read().unwrap().length, Ordering::Relaxed); 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 { } else {
self.phrase = None; self.phrase = None;
self.time_clamp.store(0, Ordering::Relaxed); 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 { pub trait HasEditor {
fn editor (&self) -> &PhraseEditorModel; fn editor (&self) -> &PhraseEditorModel;
fn editor_focused (&self) -> bool; fn editor_focused (&self) -> bool;

View file

@ -1,14 +1,14 @@
use crate::*; use crate::*;
pub struct PhraseView<'a> { pub struct PhraseView<'a> {
focused: bool, focused: bool,
entered: bool, entered: bool,
phrase: &'a Option<Arc<RwLock<Phrase>>>, phrase: &'a Option<Arc<RwLock<Phrase>>>,
buffer: &'a BigBuffer, buffer: &'a BigBuffer,
note_len: usize, note_len: usize,
now: &'a Arc<Pulse>, now: &'a Arc<Pulse>,
size: &'a Measure<Tui>,
size: &'a Measure<Tui>, view_mode: &'a PhraseViewMode,
note_point: usize, note_point: usize,
note_range: (usize, usize), note_range: (usize, usize),
@ -42,14 +42,14 @@ impl<'a, T: HasEditor> From<&'a T> for PhraseView<'a> {
} }
Self { Self {
focused: state.editor_focused(), focused: state.editor_focused(),
entered: state.editor_entered(), entered: state.editor_entered(),
note_len: editor.note_len, note_len: editor.note_len,
phrase: &editor.phrase, phrase: &editor.phrase,
buffer: &editor.buffer, buffer: &editor.buffer,
now: &editor.now, now: &editor.now,
size: &editor.size,
size: &editor.size, view_mode: &editor.view_mode,
note_point, note_point,
note_range: (note_lo, note_hi), note_range: (note_lo, note_hi),
@ -72,6 +72,7 @@ impl<'a> Content for PhraseView<'a> {
phrase, phrase,
size, size,
buffer, buffer,
view_mode,
note_len, note_len,
note_range: (note_lo, note_hi), note_range: (note_lo, note_hi),
note_names: (note_lo_name, note_hi_name), 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); size.set_wh(area.w(), h - 1);
Ok(if to.area().h() >= 2 { Ok(if to.area().h() >= 2 {
let area = to.area(); let area = to.area();
to.buffer_update(area, &move |cell, x, y|{ view_mode.blit(buffer, &mut to.buffer, area, *time_start, *note_hi);
cell.set_bg(notes_bg_null); //to.buffer_update(area, &move |cell, x, y|{
let src_x = (x as usize + time_start) * time_scale; //cell.set_bg(notes_bg_null);
let src_y = y as usize + note_lo / 2; //let src_x = (x as usize + time_start) * time_scale;
if src_x < buffer.width && src_y < buffer.height - 1 { //let src_y = y as usize + note_lo / 2;
buffer.get(src_x, (note_hi - y as usize) / 2).map(|src|{ //if src_x < buffer.width && src_y < buffer.height - 1 {
cell.set_symbol(src.symbol()); //buffer.get(src_x, (note_hi - y as usize) / 2).map(|src|{
cell.set_fg(src.fg); //cell.set_symbol(src.symbol());
cell.set_bg(src.bg); //cell.set_fg(src.fg);
}); //cell.set_bg(src.bg);
} //});
}); //}
//});
}) })
}; };
let cursor = move|to: &mut TuiOutput|Ok(if *focused && *entered { let cursor = move|to: &mut TuiOutput|Ok(if *focused && *entered {
@ -230,27 +232,53 @@ pub enum PhraseViewMode {
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum PhraseViewNoteZoom { pub enum PhraseViewNoteZoom {
Half,
N(usize), N(usize),
Half,
Octant,
} }
impl PhraseViewMode { impl PhraseViewMode {
/// Return a new [BigBuffer] containing a render of the phrase. /// 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)); let mut buffer = BigBuffer::new(self.buffer_width(phrase), self.buffer_height(phrase));
match self { match self {
Self::PianoHorizontal { time_zoom, note_zoom } => match note_zoom { 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( PhraseViewNoteZoom::N(_) => Self::draw_piano_horizontal(
&mut buffer, phrase, *time_zoom, 1 &mut buffer, phrase, *time_zoom, 1
), ),
_ => unimplemented!(),
}, },
_ => unimplemented!(), _ => unimplemented!(),
} }
buffer 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. /// Determine the required width to render the phrase.
fn buffer_width (&self, phrase: &Phrase) -> usize { fn buffer_width (&self, phrase: &Phrase) -> usize {
match self { match self {
@ -260,6 +288,7 @@ impl PhraseViewMode {
Self::PianoVertical { note_zoom, .. } => match note_zoom { Self::PianoVertical { note_zoom, .. } => match note_zoom {
PhraseViewNoteZoom::Half => 64, PhraseViewNoteZoom::Half => 64,
PhraseViewNoteZoom::N(n) => 128*n, PhraseViewNoteZoom::N(n) => 128*n,
_ => unimplemented!()
}, },
} }
} }
@ -269,19 +298,16 @@ impl PhraseViewMode {
Self::PianoHorizontal { note_zoom, .. } => match note_zoom { Self::PianoHorizontal { note_zoom, .. } => match note_zoom {
PhraseViewNoteZoom::Half => 64, PhraseViewNoteZoom::Half => 64,
PhraseViewNoteZoom::N(n) => 128*n, PhraseViewNoteZoom::N(n) => 128*n,
_ => unimplemented!()
}, },
Self::PianoVertical { time_zoom, .. } => { Self::PianoVertical { time_zoom, .. } => {
phrase.length / time_zoom phrase.length / time_zoom
}, },
} }
} }
fn draw_piano_horizontal_half ( /// Draw the piano roll using full blocks on note on and half blocks on legato: █▄ █▄ █▄
_: &mut BigBuffer, _: &Phrase, _: usize
) {
unimplemented!()
}
fn draw_piano_horizontal ( 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]; let mut notes_on = [false;128];
for col in 0..buffer.width { for col in 0..buffer.width {
@ -295,6 +321,8 @@ impl PhraseViewMode {
} else { } else {
cell.set_char(' '); 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() { for event in phrase.notes[time].iter() {
match event { 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!() unimplemented!()
} }
} }