wip: full block piano roll

This commit is contained in:
🪞👃🪞 2024-12-03 21:53:34 +01:00
parent 385d95f800
commit 64ac577f4a
2 changed files with 199 additions and 72 deletions

View file

@ -26,6 +26,7 @@ pub struct PhraseEditorModel {
pub(crate) time_scale: AtomicUsize, pub(crate) time_scale: AtomicUsize,
pub(crate) edit_mode: PhraseEditMode, pub(crate) edit_mode: PhraseEditMode,
pub(crate) view_mode: PhraseViewMode,
} }
impl std::fmt::Debug for PhraseEditorModel { impl std::fmt::Debug for PhraseEditorModel {
@ -56,6 +57,7 @@ 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(),
@ -109,6 +111,13 @@ pub enum PhraseEditMode {
Scroll, 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

@ -216,88 +216,206 @@ impl<'a> Content for PhraseView<'a> {
} }
} }
//const NTH_OCTAVE: [&'static str; 11] = [ #[derive(Copy, Clone, Debug)]
//"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "X", pub enum PhraseViewMode {
////"-2", "-1", "0", "1", "2", "3", "4", "5", "6", "7", "8", PianoHorizontal {
//]; time_zoom: usize,
note_zoom: PhraseViewNoteZoom,
},
PianoVertical {
time_zoom: usize,
note_zoom: PhraseViewNoteZoom,
},
}
impl PhraseEditorModel { #[derive(Copy, Clone, Debug)]
pub(crate) fn redraw (phrase: &Phrase) -> BigBuffer { pub enum PhraseViewNoteZoom {
let mut buf = BigBuffer::new(usize::MAX.min(phrase.length), 65); Half,
Self::fill_seq_bg(&mut buf, phrase.length, phrase.ppq); N(usize),
Self::fill_seq_fg(&mut buf, &phrase); }
buf
impl PhraseViewMode {
/// Return a new [BigBuffer] containing a render of the phrase.
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!(),
}
buffer
} }
fn fill_seq_bg (buf: &mut BigBuffer, length: usize, ppq: usize) { /// Determine the required width to render the phrase.
for x in 0..buf.width { fn buffer_width (&self, phrase: &Phrase) -> usize {
// Only fill as far as phrase length match self {
if x as usize >= length { break } Self::PianoHorizontal { time_zoom, .. } => {
// Fill each row with background characters phrase.length / time_zoom
for y in 0..buf.height { },
if y >= 64 { Self::PianoVertical { note_zoom, .. } => match note_zoom {
break PhraseViewNoteZoom::Half => 64,
} PhraseViewNoteZoom::N(n) => 128*n,
buf.get_mut(x, y).map(|cell|{ },
cell.set_char(if ppq == 0 {
'·'
} else if x % (4 * ppq) == 0 {
'│'
} else if x % ppq == 0 {
'╎'
} else {
'·'
});
cell.set_fg(Color::Rgb(48, 64, 56));
cell.modifier = Modifier::DIM;
});
}
} }
} }
fn fill_seq_fg (buf: &mut BigBuffer, phrase: &Phrase) { /// Determine the required height to render the phrase.
fn buffer_height (&self, phrase: &Phrase) -> usize {
match self {
Self::PianoHorizontal { note_zoom, .. } => match note_zoom {
PhraseViewNoteZoom::Half => 64,
PhraseViewNoteZoom::N(n) => 128*n,
},
Self::PianoVertical { time_zoom, .. } => {
phrase.length / time_zoom
},
}
}
fn draw_piano_horizontal_half (
_: &mut BigBuffer, _: &Phrase, _: usize
) {
unimplemented!()
}
fn draw_piano_horizontal (
buffer: &mut BigBuffer, phrase: &Phrase, time_zoom: usize, note_zoom: usize
) {
let mut notes_on = [false;128]; let mut notes_on = [false;128];
for x in 0..buf.width { for col in 0..buffer.width {
if x as usize >= phrase.length { let time_start = time_zoom * col;
break let time_end = time_zoom * (col + 1);
} for time in time_start..time_end {
if let Some(notes) = phrase.notes.get(x as usize) { for row in 0..buffer.height {
if phrase.percussive { let cell = buffer.get_mut(row, col).unwrap();
for note in notes { if notes_on[row] {
match note { cell.set_char('▄');
MidiMessage::NoteOn { key, .. } => } else {
notes_on[key.as_int() as usize] = true, cell.set_char(' ');
_ => {}
}
}
} 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 { for event in phrase.notes[time].iter() {
if y > 63 { match event {
break MidiMessage::NoteOn { key, .. } => {
let row = key.as_int() as usize;
buffer.get_mut(row, col).unwrap().set_char('█');
notes_on[row] = true
},
MidiMessage::NoteOff { key, .. } => {
notes_on[key.as_int() as usize] = false
},
_ => {}
} }
let y = 63 - y;
if let Some(block) = half_block(
notes_on[y as usize * 2 + 1],
notes_on[y as usize * 2],
) {
buf.get_mut(x, y).map(|cell|{
cell.set_char(block);
cell.set_fg(Color::White);
});
}
}
if phrase.percussive {
notes_on.fill(false);
} }
} }
} }
//for row in 0..buf.height {
//let pitch = 127 - row;
//}
unimplemented!()
} }
} }
//impl PhraseEditorModel {
//pub(crate) fn redraw (phrase: &Phrase, mode: PhraseViewMode) -> BigBuffer {
//let mut buf = BigBuffer::new(usize::MAX.min(phrase.length), 65);
//Self::fill_seq_bg(mode, &mut buf, phrase.length, phrase.ppq);
//Self::fill_seq_fg(mode, &mut buf, &phrase);
//buf
//}
//fn fill_seq_bg (_mode: PhraseViewMode, buf: &mut BigBuffer, length: usize, ppq: usize) {
//for x in 0..buf.width {
//// Only fill as far as phrase length
//if x as usize >= length { break }
//// Fill each row with background characters
//for y in 0..buf.height {
//if y >= 64 {
//break
//}
//buf.get_mut(x, y).map(|cell|{
//cell.set_char(if ppq == 0 {
//'·'
//} else if x % (4 * ppq) == 0 {
//'│'
//} else if x % ppq == 0 {
//'╎'
//} else {
//'·'
//});
//cell.set_fg(Color::Rgb(48, 64, 56));
//cell.modifier = Modifier::DIM;
//});
//}
//}
//}
//fn fill_seq_fg (mode: PhraseViewMode, buf: &mut BigBuffer, phrase: &Phrase) {
//match mode {
//PhraseViewMode::Horizontal =>
//Self::fill_seq_fg_horizontal(buf, phrase),
//PhraseViewMode::HorizontalHalf =>
//Self::fill_seq_fg_horizontal_half(buf, phrase),
//PhraseViewMode::Vertical =>
//Self::fill_seq_fg_vertical(buf, phrase),
//}
//}
//fn fill_seq_fg_horizontal (buf: &mut BigBuffer, phrase: &Phrase) {
//let mut notes_on = [false;128];
//for x in 0..buf.width {
//}
//}
//fn fill_seq_fg_horizontal_half (buf: &mut BigBuffer, phrase: &Phrase) {
//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 {
//if y > 63 {
//break
//}
//let y = 63 - y;
//if let Some(block) = half_block(
//notes_on[y as usize * 2 + 1],
//notes_on[y as usize * 2],
//) {
//buf.get_mut(x, y).map(|cell|{
//cell.set_char(block);
//cell.set_fg(Color::White);
//});
//}
//}
//if phrase.percussive {
//notes_on.fill(false);
//}
//}
//}
//}
//}
////const NTH_OCTAVE: [&'static str; 11] = [
////"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "X",
//////"-2", "-1", "0", "1", "2", "3", "4", "5", "6", "7", "8",
////];