//! MIDI editor. use crate::*; /// Contains state for viewing and editing a clip pub struct MidiEditor { /// Size of editor on screen pub size: Measure, /// View mode and state of editor pub mode: PianoHorizontal, /// Input keymap pub keys: InputMap<'static, Self, MidiEditCommand, TuiIn, SourceIter<'static>> } impl std::fmt::Debug for MidiEditor { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { f.debug_struct("MidiEditor") .field("mode", &self.mode) .finish() } } impl Default for MidiEditor { fn default () -> Self { Self { size: Measure::new(), mode: PianoHorizontal::new(None), keys: InputMap::new(SourceIter(include_str!("../../../../config/keys_editor.edn"))), } } } has_size!(|self: MidiEditor|&self.size); content!(TuiOut: |self: MidiEditor| { self.autoscroll(); //self.autozoom(); self.size.of(&self.mode) }); from!(|clip: &Arc>|MidiEditor = { let model = Self::from(Some(clip.clone())); model.redraw(); model }); from!(|clip: Option>>|MidiEditor = { let mut model = Self::default(); *model.clip_mut() = clip; model.redraw(); model }); impl MidiEditor { /// Put note at current position pub fn put_note (&mut self, advance: bool) { let mut redraw = false; if let Some(clip) = self.clip() { let mut clip = clip.write().unwrap(); let note_start = self.time_pos(); let note_pos = self.note_pos(); let note_len = self.note_len(); let note_end = note_start + (note_len.saturating_sub(1)); let key: u7 = u7::from(note_pos as u8); let vel: u7 = 100.into(); let length = clip.length; let note_end = note_end % length; let note_on = MidiMessage::NoteOn { key, vel }; if !clip.notes[note_start].iter().any(|msg|*msg == note_on) { clip.notes[note_start].push(note_on); } let note_off = MidiMessage::NoteOff { key, vel }; if !clip.notes[note_end].iter().any(|msg|*msg == note_off) { clip.notes[note_end].push(note_off); } if advance { self.set_time_pos(note_end); } redraw = true; } if redraw { self.mode.redraw(); } } pub fn clip_status (&self) -> impl Content + '_ { let (color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) { (clip.color, clip.name.clone(), clip.length, clip.looped) } else { (ItemTheme::G[64], String::new().into(), 0, false) }; Bsp::e( FieldH(color, "Edit", format!("{name} ({length})")), FieldH(color, "Loop", looped.to_string()) ) } pub fn edit_status (&self) -> impl Content + '_ { let (color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) { (clip.color, clip.length) } else { (ItemTheme::G[64], 0) }; let time_pos = self.time_pos(); let time_zoom = self.time_zoom().get(); let time_lock = if self.time_lock().get() { "[lock]" } else { " " }; let note_pos = format!("{:>3}", self.note_pos()); let note_name = format!("{:4}", Note::pitch_to_name(self.note_pos())); let note_len = format!("{:>4}", self.note_len()); Bsp::e( FieldH(color, "Time", format!("{length}/{time_zoom}+{time_pos} {time_lock}")), FieldH(color, "Note", format!("{note_name} {note_pos} {note_len}")), ) } //fn clip_length (&self) -> usize { //self.clip().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1) //} } impl TimeRange for MidiEditor { fn time_len (&self) -> &AtomicUsize { self.mode.time_len() } fn time_zoom (&self) -> &AtomicUsize { self.mode.time_zoom() } fn time_lock (&self) -> &AtomicBool { self.mode.time_lock() } fn time_start (&self) -> &AtomicUsize { self.mode.time_start() } fn time_axis (&self) -> &AtomicUsize { self.mode.time_axis() } } impl NoteRange for MidiEditor { fn note_lo (&self) -> &AtomicUsize { self.mode.note_lo() } fn note_axis (&self) -> &AtomicUsize { self.mode.note_axis() } } impl NotePoint for MidiEditor { fn note_len (&self) -> usize { self.mode.note_len() } fn set_note_len (&self, x: usize) -> usize { self.mode.set_note_len(x) } fn note_pos (&self) -> usize { self.mode.note_pos() } fn set_note_pos (&self, x: usize) -> usize { self.mode.set_note_pos(x) } } impl TimePoint for MidiEditor { fn time_pos (&self) -> usize { self.mode.time_pos() } fn set_time_pos (&self, x: usize) -> usize { self.mode.set_time_pos(x) } } impl MidiViewer for MidiEditor { fn buffer_size (&self, clip: &MidiClip) -> (usize, usize) { self.mode.buffer_size(clip) } fn redraw (&self) { self.mode.redraw() } fn clip (&self) -> &Option>> { self.mode.clip() } fn clip_mut (&mut self) -> &mut Option>> { self.mode.clip_mut() } fn set_clip (&mut self, p: Option<&Arc>>) { self.mode.set_clip(p) } }