diff --git a/crates/tek_tui/src/tui_model_phrase_editor.rs b/crates/tek_tui/src/tui_model_phrase_editor.rs index adba5411..205cbfa6 100644 --- a/crates/tek_tui/src/tui_model_phrase_editor.rs +++ b/crates/tek_tui/src/tui_model_phrase_editor.rs @@ -26,6 +26,7 @@ pub struct PhraseEditorModel { pub(crate) time_scale: AtomicUsize, pub(crate) edit_mode: PhraseEditMode, + pub(crate) view_mode: PhraseViewMode, } impl std::fmt::Debug for PhraseEditorModel { @@ -56,6 +57,7 @@ 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(), @@ -109,6 +111,13 @@ pub enum PhraseEditMode { Scroll, } +#[derive(Copy, Clone, Debug)] +pub enum PhraseViewMode { + Horizontal, + HorizontalHalf, + Vertical, +} + pub trait HasEditor { fn editor (&self) -> &PhraseEditorModel; fn editor_focused (&self) -> bool; diff --git a/crates/tek_tui/src/tui_view_phrase_editor.rs b/crates/tek_tui/src/tui_view_phrase_editor.rs index 97a73ad0..55229bab 100644 --- a/crates/tek_tui/src/tui_view_phrase_editor.rs +++ b/crates/tek_tui/src/tui_view_phrase_editor.rs @@ -216,88 +216,206 @@ impl<'a> Content for PhraseView<'a> { } } -//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", -//]; +#[derive(Copy, Clone, Debug)] +pub enum PhraseViewMode { + PianoHorizontal { + time_zoom: usize, + note_zoom: PhraseViewNoteZoom, + }, + PianoVertical { + time_zoom: usize, + note_zoom: PhraseViewNoteZoom, + }, +} -impl PhraseEditorModel { - pub(crate) fn redraw (phrase: &Phrase) -> BigBuffer { - let mut buf = BigBuffer::new(usize::MAX.min(phrase.length), 65); - Self::fill_seq_bg(&mut buf, phrase.length, phrase.ppq); - Self::fill_seq_fg(&mut buf, &phrase); - buf +#[derive(Copy, Clone, Debug)] +pub enum PhraseViewNoteZoom { + Half, + N(usize), +} + +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) { - 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; - }); - } + /// Determine the required width to render the phrase. + fn buffer_width (&self, phrase: &Phrase) -> usize { + match self { + Self::PianoHorizontal { time_zoom, .. } => { + phrase.length / time_zoom + }, + Self::PianoVertical { note_zoom, .. } => match note_zoom { + PhraseViewNoteZoom::Half => 64, + PhraseViewNoteZoom::N(n) => 128*n, + }, } } - 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]; - 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 col in 0..buffer.width { + let time_start = time_zoom * col; + let time_end = time_zoom * (col + 1); + for time in time_start..time_end { + for row in 0..buffer.height { + let cell = buffer.get_mut(row, col).unwrap(); + if notes_on[row] { + cell.set_char('▄'); + } else { + cell.set_char(' '); } } - for y in 0..buf.height { - if y > 63 { - break + for event in phrase.notes[time].iter() { + match event { + 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", +////]; +