mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-10-08 18:56:42 +02:00
This commit is contained in:
parent
186b81f29c
commit
ca774cca41
21 changed files with 2160 additions and 2191 deletions
214
src/midi_edit.rs
214
src/midi_edit.rs
|
|
@ -26,9 +26,10 @@ impl<T> HasEditor for T where T: AsRef<Option<MidiEditor>> + AsMut<Option<MidiEd
|
|||
/// ```
|
||||
/// use std::sync::Arc;
|
||||
/// use parking_lot::RwLock;
|
||||
/// let clip = tek::MidiClip::stop_all();
|
||||
/// let mode = tek::PianoHorizontal::new(Some(&Arc::new(RwLock::new(clip))));
|
||||
/// let mut editor = tek::MidiEditor { mode, size: Default::default() };
|
||||
/// use tek::{MidiClip, MidiEditor, PianoHorizontal};
|
||||
/// let clip = MidiClip::stop_all();
|
||||
/// let mode = PianoHorizontal::default();
|
||||
/// let mut editor = MidiEditor { mode, size: Default::default() };
|
||||
///
|
||||
/// let _ = editor.put_note(true);
|
||||
/// let _ = editor.put_note(false);
|
||||
|
|
@ -43,7 +44,7 @@ pub struct MidiEditor {
|
|||
}
|
||||
|
||||
impl_default!(MidiEditor: Self {
|
||||
mode: PianoHorizontal::new(None),
|
||||
mode: Default::default(),
|
||||
size: Sizer(
|
||||
Arc::new(0usize.into()),
|
||||
Arc::new(0usize.into()),
|
||||
|
|
@ -54,14 +55,14 @@ 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.try_write().unwrap();
|
||||
if let Some(clip) = &*self.mode.clip.read() {
|
||||
let note_start = self.get_time_pos();
|
||||
let note_pos = self.get_note_pos();
|
||||
let note_len = self.get_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 mut clip = clip.write();
|
||||
let length = clip.length;
|
||||
let note_end = note_end % length;
|
||||
let note_on = MidiMessage::NoteOn { key, vel };
|
||||
|
|
@ -81,21 +82,51 @@ impl MidiEditor {
|
|||
self.mode.redraw();
|
||||
}
|
||||
}
|
||||
fn clip_length (&self) -> usize { self.clip().as_ref().map(|p|p.try_read().unwrap().length).unwrap_or(1) }
|
||||
fn note_length (&self) -> usize { self.get_note_len() }
|
||||
fn note_pos (&self) -> usize { self.get_note_pos() }
|
||||
fn note_pos_next (&self) -> usize { self.get_note_pos() + 1 }
|
||||
fn note_pos_next_octave (&self) -> usize { self.get_note_pos() + 12 }
|
||||
fn note_pos_prev (&self) -> usize { self.get_note_pos().saturating_sub(1) }
|
||||
fn note_pos_prev_octave (&self) -> usize { self.get_note_pos().saturating_sub(12) }
|
||||
fn note_len (&self) -> usize { self.get_note_len() }
|
||||
fn note_len_next (&self) -> usize { self.get_note_len() + 1 }
|
||||
fn note_len_prev (&self) -> usize { self.get_note_len().saturating_sub(1) }
|
||||
fn note_range (&self) -> usize { self.get_note_axis() }
|
||||
fn note_range_next (&self) -> usize { self.get_note_axis() + 1 }
|
||||
fn note_range_prev (&self) -> usize { self.get_note_axis().saturating_sub(1) }
|
||||
fn time_zoom (&self) -> usize { self.get_time_zoom() }
|
||||
fn time_zoom_next (&self) -> usize { self.get_time_zoom() + 1 }
|
||||
fn clip_length (&self) -> usize {
|
||||
self.mode.clip.map_inner(|p|p.length).unwrap_or(1)
|
||||
}
|
||||
fn note_length (&self) -> usize {
|
||||
self.get_note_len()
|
||||
}
|
||||
fn note_pos (&self) -> usize {
|
||||
self.get_note_pos()
|
||||
}
|
||||
fn note_pos_next (&self) -> usize {
|
||||
self.get_note_pos() + 1
|
||||
}
|
||||
fn note_pos_next_octave (&self) -> usize {
|
||||
self.get_note_pos() + 12
|
||||
}
|
||||
fn note_pos_prev (&self) -> usize {
|
||||
self.get_note_pos().saturating_sub(1)
|
||||
}
|
||||
fn note_pos_prev_octave (&self) -> usize {
|
||||
self.get_note_pos().saturating_sub(12)
|
||||
}
|
||||
fn note_len (&self) -> usize {
|
||||
self.get_note_len()
|
||||
}
|
||||
fn note_len_next (&self) -> usize {
|
||||
self.get_note_len() + 1
|
||||
}
|
||||
fn note_len_prev (&self) -> usize {
|
||||
self.get_note_len().saturating_sub(1)
|
||||
}
|
||||
fn note_range (&self) -> usize {
|
||||
self.get_note_axis()
|
||||
}
|
||||
fn note_range_next (&self) -> usize {
|
||||
self.get_note_axis() + 1
|
||||
}
|
||||
fn note_range_prev (&self) -> usize {
|
||||
self.get_note_axis().saturating_sub(1)
|
||||
}
|
||||
fn time_zoom (&self) -> usize {
|
||||
self.get_time_zoom()
|
||||
}
|
||||
fn time_zoom_next (&self) -> usize {
|
||||
self.get_time_zoom() + 1
|
||||
}
|
||||
fn time_zoom_next_fine (&self) -> usize { self.get_time_zoom() + 1 }
|
||||
fn time_zoom_prev (&self) -> usize { self.get_time_zoom().saturating_sub(1).max(1) }
|
||||
fn time_zoom_prev_fine (&self) -> usize { self.get_time_zoom().saturating_sub(1).max(1) }
|
||||
|
|
@ -113,39 +144,6 @@ impl MidiEditor {
|
|||
self.get_time_pos().overflowing_sub(1)
|
||||
.0.min(self.clip_length().saturating_sub(1))
|
||||
}
|
||||
pub fn clip_status (&self) -> impl Draw<Tui> + '_ {
|
||||
let (_color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.try_read().unwrap()) {
|
||||
(clip.color, clip.name.clone(), clip.length, clip.looped)
|
||||
} else { (ItemTheme::G[64], String::new().into(), 0, false) };
|
||||
south!(
|
||||
east(button_2("f2", "name ", false), fg(Rgb(255, 255, 255), format!("{name} "))
|
||||
.origin_e().full_w()).origin_w().full_w(),
|
||||
east(button_2("l", "ength ", false), fg(Rgb(255, 255, 255), format!("{length} ")).origin_e().full_w())
|
||||
.origin_w().full_w(),
|
||||
east(button_2("r", "epeat ", false), fg(Rgb(255, 255, 255), format!("{looped} ")).origin_e().full_w())
|
||||
.origin_w().full_w(),
|
||||
).exact_w(20)
|
||||
}
|
||||
pub fn edit_status (&self) -> impl Draw<Tui> + '_ {
|
||||
let (_color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.try_read().unwrap()) {
|
||||
(clip.color, clip.length)
|
||||
} else { (ItemTheme::G[64], 0) };
|
||||
let time_pos = self.get_time_pos();
|
||||
let time_zoom = self.get_time_zoom();
|
||||
let time_lock = if self.get_time_lock() { "[lock]" } else { " " };
|
||||
let note_pos = self.get_note_pos();
|
||||
let note_name = format!("{:4}", note_pitch_to_name(note_pos));
|
||||
let note_pos = format!("{:>3}", note_pos);
|
||||
let note_len = format!("{:>4}", self.get_note_len());
|
||||
south!(
|
||||
east(button_2("t", "ime ", false),
|
||||
fg(Rgb(255, 255, 255), format!("{length} /{time_zoom} +{time_pos} ")).origin_e().full_w()).origin_w().full_w(),
|
||||
east(button_2("z", "lock ", false),
|
||||
fg(Rgb(255, 255, 255), format!("{time_lock}")).origin_e().full_w()).origin_w().full_w(),
|
||||
east(button_2("x", "note ", false),
|
||||
fg(Rgb(255, 255, 255), format!("{note_name} {note_pos} {note_len}")).origin_e().full_w()).origin_w().full_w(),
|
||||
).exact_w(20)
|
||||
}
|
||||
}
|
||||
|
||||
/// ```
|
||||
|
|
@ -170,12 +168,6 @@ impl <T: TimeRange+NoteRange> MidiRange for T {}
|
|||
pub trait MidiViewer: MidiRange + MidiPoint + Debug + Send + Sync {
|
||||
fn buffer_size (&self, clip: &MidiClip) -> (usize, usize);
|
||||
fn redraw (&self);
|
||||
fn clip (&self) -> &Option<Arc<RwLock<MidiClip>>>;
|
||||
fn clip_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>>;
|
||||
fn set_clip (&mut self, clip: Option<&Arc<RwLock<MidiClip>>>) {
|
||||
*self.clip_mut() = clip.cloned();
|
||||
self.redraw();
|
||||
}
|
||||
/// Make sure cursor is within note range
|
||||
fn autoscroll (&self) {
|
||||
let note_pos = self.get_note_pos().min(127);
|
||||
|
|
@ -240,19 +232,6 @@ impl std::fmt::Debug for MidiEditor {
|
|||
}
|
||||
}
|
||||
|
||||
impl_from!(MidiEditor: |clip: &Arc<RwLock<MidiClip>>| {
|
||||
let model = Self::from(Some(clip.clone()));
|
||||
model.redraw();
|
||||
model
|
||||
});
|
||||
|
||||
impl_from!(MidiEditor: |clip: Option<Arc<RwLock<MidiClip>>>| {
|
||||
let mut model = Self::default();
|
||||
*model.clip_mut() = clip;
|
||||
model.redraw();
|
||||
model
|
||||
});
|
||||
|
||||
impl NotePoint for MidiEditor {
|
||||
fn note_len (&self) -> &AtomicUsize { self.mode.note_len() }
|
||||
fn note_pos (&self) -> &AtomicUsize { self.mode.note_pos() }
|
||||
|
|
@ -263,31 +242,15 @@ impl TimePoint for MidiEditor {
|
|||
}
|
||||
|
||||
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<Arc<RwLock<MidiClip>>> { self.mode.clip() }
|
||||
fn clip_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>> { self.mode.clip_mut() }
|
||||
fn set_clip (&mut self, p: Option<&Arc<RwLock<MidiClip>>>) { self.mode.set_clip(p) }
|
||||
}
|
||||
|
||||
impl Draw<Tui> for MidiEditor {
|
||||
fn draw (&self, to: &mut Tui) -> Drawn<u16> {
|
||||
self.autoscroll();
|
||||
/*self.autozoom();*/
|
||||
self.size.of(&self.mode).draw(to)
|
||||
fn buffer_size (&self, clip: &MidiClip) -> (usize, usize) {
|
||||
self.mode.buffer_size(clip)
|
||||
}
|
||||
fn redraw (&self) {
|
||||
self.mode.redraw()
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
/// Is a MIDI editor currently focused?
|
||||
///
|
||||
/// ```
|
||||
/// tek::App::default().editor_focused();
|
||||
/// ```
|
||||
pub fn editor_focused (&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Toggle MIDI editor.
|
||||
///
|
||||
/// ```
|
||||
|
|
@ -295,39 +258,50 @@ impl App {
|
|||
/// ```
|
||||
pub fn toggle_editor (&mut self, value: Option<bool>) {
|
||||
//FIXME: self.editing.store(value.unwrap_or_else(||!self.is_editing()), Relaxed);
|
||||
let value = value.unwrap_or_else(||!self.editor().is_some());
|
||||
if value {
|
||||
// Create new clip in pool when entering empty cell
|
||||
if let Selection::TrackClip { track, scene } = *self.selection()
|
||||
&& let Some(scene) = self.project.scenes.get_mut(scene)
|
||||
&& let Some(slot) = scene.clips.get_mut(track)
|
||||
&& slot.is_none()
|
||||
&& let Some(track) = self.project.tracks.get_mut(track)
|
||||
{
|
||||
let (_index, clip) = self.project.pool.add_new_clip();
|
||||
// autocolor: new clip colors from scene and track color
|
||||
let color = track.color.base.mix(scene.color.base, 0.5);
|
||||
clip.try_write().unwrap().color = ItemColor::random_near(color, 0.2).into();
|
||||
if let Some(editor) = &mut self.project.editor {
|
||||
editor.set_clip(Some(&clip));
|
||||
}
|
||||
*slot = Some(clip.clone());
|
||||
//Some(clip)
|
||||
} else {
|
||||
//None
|
||||
if value.unwrap_or_else(||!self.editor().is_some()) {
|
||||
self.toggle_editor_on()
|
||||
} else {
|
||||
self.toggle_editor_off()
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_editor_on (&mut self) {
|
||||
// Create new clip in pool when entering empty cell
|
||||
if let Selection::TrackClip { track: track_index, scene } = *self.selection()
|
||||
&& let Some(scene) = self.project.scenes.get_mut(scene)
|
||||
&& scene.clips.get(track_index).is_none()
|
||||
&& let Some(track) = self.project.tracks.get_mut(track_index)
|
||||
{
|
||||
let color = track.color.base.mix(scene.color.base, 0.5);
|
||||
let color = ItemColor::random_near(color, 0.2).into();
|
||||
let clip: MidiClip = MidiClip { color, ..Default::default() };
|
||||
let clip = self.project.clips.append(Some(clip));
|
||||
// autocolor: new clip colors from scene and track color
|
||||
if let Some(editor) = &mut self.project.editor {
|
||||
*editor.mode.clip.write() = clip.0.read().clone();
|
||||
editor.mode.color = clip.0.read().as_ref().map(|p|p.read().color)
|
||||
.unwrap_or(ItemTheme::G[64]);
|
||||
}
|
||||
} else if let Selection::TrackClip { track, scene } = *self.selection()
|
||||
*scene.clips.slot(track_index).write() = clip.0.read().clone();
|
||||
//Some(clip)
|
||||
} else {
|
||||
//None
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_editor_off (&mut self) {
|
||||
if let Selection::TrackClip { track, scene } = *self.selection()
|
||||
&& let Some(scene) = self.project.scenes.get_mut(scene)
|
||||
&& let Some(slot) = scene.clips.get_mut(track)
|
||||
&& let Some(clip) = slot.as_mut()
|
||||
{
|
||||
// Remove clip from arrangement when exiting empty clip editor
|
||||
let mut swapped = None;
|
||||
if clip.try_read().unwrap().count_midi_messages() == 0 {
|
||||
std::mem::swap(&mut swapped, slot);
|
||||
if let Some(count) = scene.clips.slot(track).read().as_ref().map(|c|c.read().count_midi_messages())
|
||||
&& count == 0
|
||||
{
|
||||
std::mem::swap(&mut swapped, &mut scene.clips.slot(track).write());
|
||||
}
|
||||
if let Some(clip) = swapped {
|
||||
self.project.pool.delete_clip(&clip.try_read().unwrap());
|
||||
self.project.clips.remove(&*clip.read());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue