mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
wip: full block piano roll
This commit is contained in:
parent
385d95f800
commit
64ac577f4a
2 changed files with 199 additions and 72 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
////];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue