mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
fix editor behaviors
This commit is contained in:
parent
6408cd26b8
commit
968441850f
8 changed files with 151 additions and 148 deletions
|
|
@ -1,12 +1,12 @@
|
|||
(@up note/pos :note-pos-next)
|
||||
(@down note/pos :note-pos-prev)
|
||||
(@comma note/len :note-length-prev)
|
||||
(@period note/len :note-length-next)
|
||||
(@comma note/len :note-len-prev)
|
||||
(@period note/len :note-len-next)
|
||||
(@plus note/range :note-range-next-)
|
||||
(@underscore note/range :note-range-prev-)
|
||||
|
||||
(@left time/pos :time-pos-next)
|
||||
(@right time/pos :time-pos-prev)
|
||||
(@left time/pos :time-pos-prev)
|
||||
(@right time/pos :time-pos-next)
|
||||
(@equal time/zoom :time-zoom-prev)
|
||||
(@minus time/zoom :time-zoom-next)
|
||||
(@z time/lock)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
use crate::*;
|
||||
use self::MidiEditCommand::*;
|
||||
use KeyCode::*;
|
||||
pub trait HasEditor {
|
||||
fn editor (&self) -> &Option<MidiEditor>;
|
||||
fn editor_mut (&mut self) -> &Option<MidiEditor>;
|
||||
|
|
@ -52,21 +50,33 @@ from!(|clip: Option<Arc<RwLock<MidiClip>>>|MidiEditor = {
|
|||
model
|
||||
});
|
||||
edn_provide!(bool: |self: MidiEditor| {
|
||||
":true" => true,
|
||||
":false" => false
|
||||
":true" => true,
|
||||
":false" => false,
|
||||
":time-lock" => self.time_lock().get(),
|
||||
":time-lock-toggle" => !self.time_lock().get(),
|
||||
});
|
||||
edn_provide!(usize: |self: MidiEditor| {
|
||||
":note-length" => self.note_len(),
|
||||
":note-length" => self.note_len(),
|
||||
|
||||
":note-pos" => self.note_point(),
|
||||
":note-pos-next" => self.note_point() + 1,
|
||||
":note-pos-prev" => self.note_point().saturating_sub(1),
|
||||
":note-pos" => self.note_pos(),
|
||||
":note-pos-next" => self.note_pos() + 1,
|
||||
":note-pos-prev" => self.note_pos().saturating_sub(1),
|
||||
|
||||
":time-pos" => self.time_point(),
|
||||
":time-pos-next" => self.time_point() + self.time_zoom().get(),
|
||||
":time-pos-prev" => self.time_point().saturating_sub(self.time_zoom().get()),
|
||||
":note-len" => self.note_len(),
|
||||
":note-len-next" => self.note_len() + 1,
|
||||
":note-len-prev" => self.note_len().saturating_sub(1),
|
||||
|
||||
":note-range" => self.note_axis().get(),
|
||||
":note-range-prev" => self.note_axis().get() + 1,
|
||||
":note-range-next" => self.note_axis().get().saturating_sub(1),
|
||||
|
||||
":time-pos" => self.time_pos(),
|
||||
":time-pos-next" => self.time_pos() + self.time_zoom().get(),
|
||||
":time-pos-prev" => self.time_pos().saturating_sub(self.time_zoom().get()),
|
||||
|
||||
":time-zoom" => self.time_zoom().get(),
|
||||
":time-zoom-next" => self.time_zoom().get() + 1,
|
||||
":time-zoom-prev" => self.time_zoom().get().saturating_sub(1).max(1),
|
||||
});
|
||||
impl std::fmt::Debug for MidiEditor {
|
||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
|
|
@ -91,12 +101,12 @@ impl MidiEditor {
|
|||
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_point();
|
||||
let note_point = self.note_point();
|
||||
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_point as u8);
|
||||
let key: u7 = u7::from(note_pos as u8);
|
||||
let vel: u7 = 100.into();
|
||||
let length = clip.length;
|
||||
let note_end = note_end % length;
|
||||
|
|
@ -109,7 +119,7 @@ impl MidiEditor {
|
|||
clip.notes[note_end].push(note_off);
|
||||
}
|
||||
if advance {
|
||||
self.set_time_point(note_end);
|
||||
self.set_time_pos(note_end);
|
||||
}
|
||||
redraw = true;
|
||||
}
|
||||
|
|
@ -119,25 +129,25 @@ impl MidiEditor {
|
|||
}
|
||||
}
|
||||
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_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() }
|
||||
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() }
|
||||
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) { self.mode.set_note_len(x) }
|
||||
fn note_point (&self) -> usize { self.mode.note_point() }
|
||||
fn set_note_point (&self, x: usize) { self.mode.set_note_point(x) }
|
||||
fn note_len (&self) -> usize { self.mode.note_len() }
|
||||
fn set_note_len (&self, x: usize) { self.mode.set_note_len(x) }
|
||||
fn note_pos (&self) -> usize { self.mode.note_pos() }
|
||||
fn set_note_pos (&self, x: usize) { self.mode.set_note_pos(x) }
|
||||
}
|
||||
impl TimePoint for MidiEditor {
|
||||
fn time_point (&self) -> usize { self.mode.time_point() }
|
||||
fn set_time_point (&self, x: usize) { self.mode.set_time_point(x) }
|
||||
fn time_pos (&self) -> usize { self.mode.time_pos() }
|
||||
fn set_time_pos (&self, x: usize) { self.mode.set_time_pos(x) }
|
||||
}
|
||||
impl MidiViewer for MidiEditor {
|
||||
fn buffer_size (&self, clip: &MidiClip) -> (usize, usize) { self.mode.buffer_size(clip) }
|
||||
|
|
@ -164,15 +174,15 @@ impl MidiEditor {
|
|||
} else {
|
||||
(ItemPalette::from(TuiTheme::g(64)), 0)
|
||||
};
|
||||
let time_point = self.time_point();
|
||||
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_point = format!("{:>3}", self.note_point());
|
||||
let note_name = format!("{:4}", Note::pitch_to_name(self.note_point()));
|
||||
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(
|
||||
FieldV(color, "Time", format!("{length}/{time_zoom}+{time_point} {time_lock}")),
|
||||
FieldV(color, "Note", format!("{note_name} {note_point} {note_len}")),
|
||||
FieldV(color, "Time", format!("{length}/{time_zoom}+{time_pos} {time_lock}")),
|
||||
FieldV(color, "Note", format!("{note_name} {note_pos} {note_len}")),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -184,23 +194,8 @@ edn_command!(MidiEditCommand: |state: MidiEditor| {
|
|||
("time/pos" [a: usize] Self::SetTimeCursor(a.expect("no time cursor")))
|
||||
("time/zoom" [a: usize] Self::SetTimeZoom(a.expect("no time zoom")))
|
||||
("time/lock" [a: bool] Self::SetTimeLock(a.expect("no time lock")))
|
||||
("time/lock" [] Self::SetTimeLock(!state.time_lock().get()))
|
||||
});
|
||||
//impl EdnCommand<MidiEditor> for MidiEditCommand {
|
||||
//fn from_edn <'a> (state: &MidiEditor, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
||||
//use EdnItem::*;
|
||||
//match (head, tail) {
|
||||
//(Key("note/put"), [a]) => Self::PutNote,
|
||||
//(Key("note/del"), [a]) => Self::AppendNote,
|
||||
//(Key("note/dur"), [a]) => Self::AppendNote,
|
||||
//(Key("note/range"), [a]) => Self::AppendNote,
|
||||
//(Key("note/pos"), [a]) => Self::AppendNote,
|
||||
//(Key("time/pos"), [a]) => Self::AppendNote,
|
||||
//(Key("time/zoom"), [a]) => Self::AppendNote,
|
||||
//(Key("time/lock"), [a]) => Self::AppendNote,
|
||||
//_ => todo!()
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
#[derive(Clone, Debug)] pub enum MidiEditCommand {
|
||||
// TODO: 1-9 seek markers that by default start every 8th of the clip
|
||||
AppendNote,
|
||||
|
|
@ -241,27 +236,54 @@ handle!(TuiIn: |self: MidiEditor, input|{
|
|||
None
|
||||
})
|
||||
});
|
||||
impl Command<MidiEditor> for MidiEditCommand {
|
||||
fn execute (self, state: &mut MidiEditor) -> Perhaps<Self> {
|
||||
use MidiEditCommand::*;
|
||||
match self {
|
||||
Show(clip) => { state.set_clip(clip.as_ref()); },
|
||||
PutNote => { state.put_note(false); },
|
||||
AppendNote => { state.put_note(true); },
|
||||
SetTimeZoom(x) => { state.time_zoom().set(x); state.redraw(); },
|
||||
SetTimeLock(x) => { state.time_lock().set(x); },
|
||||
SetTimeScroll(x) => { state.time_start().set(x); },
|
||||
SetNoteScroll(x) => { state.note_lo().set(x.min(127)); },
|
||||
SetNoteLength(x) => {
|
||||
let note_len = state.note_len();
|
||||
let time_zoom = state.time_zoom().get();
|
||||
state.set_note_len(x);
|
||||
if note_len / time_zoom != x / time_zoom {
|
||||
state.redraw();
|
||||
}
|
||||
},
|
||||
SetTimeCursor(x) => { state.set_time_pos(x); },
|
||||
SetNoteCursor(note) => { state.set_note_pos(note.min(127)); },
|
||||
//_ => todo!("{:?}", self)
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
//keymap!(KEYS_MIDI_EDITOR = |s: MidiEditor, _input: Event| MidiEditCommand {
|
||||
//key(Up) => SetNoteCursor(s.note_point() + 1),
|
||||
//key(Char('w')) => SetNoteCursor(s.note_point() + 1),
|
||||
//key(Down) => SetNoteCursor(s.note_point().saturating_sub(1)),
|
||||
//key(Char('s')) => SetNoteCursor(s.note_point().saturating_sub(1)),
|
||||
//key(Left) => SetTimeCursor(s.time_point().saturating_sub(s.note_len())),
|
||||
//key(Char('a')) => SetTimeCursor(s.time_point().saturating_sub(s.note_len())),
|
||||
//key(Right) => SetTimeCursor((s.time_point() + s.note_len()) % s.clip_length()),
|
||||
//ctrl(alt(key(Up))) => SetNoteScroll(s.note_point() + 3),
|
||||
//ctrl(alt(key(Down))) => SetNoteScroll(s.note_point().saturating_sub(3)),
|
||||
//ctrl(alt(key(Left))) => SetTimeScroll(s.time_point().saturating_sub(s.time_zoom().get())),
|
||||
//ctrl(alt(key(Right))) => SetTimeScroll((s.time_point() + s.time_zoom().get()) % s.clip_length()),
|
||||
//key(Up) => SetNoteCursor(s.note_pos() + 1),
|
||||
//key(Char('w')) => SetNoteCursor(s.note_pos() + 1),
|
||||
//key(Down) => SetNoteCursor(s.note_pos().saturating_sub(1)),
|
||||
//key(Char('s')) => SetNoteCursor(s.note_pos().saturating_sub(1)),
|
||||
//key(Left) => SetTimeCursor(s.time_pos().saturating_sub(s.note_len())),
|
||||
//key(Char('a')) => SetTimeCursor(s.time_pos().saturating_sub(s.note_len())),
|
||||
//key(Right) => SetTimeCursor((s.time_pos() + s.note_len()) % s.clip_length()),
|
||||
//ctrl(alt(key(Up))) => SetNoteScroll(s.note_pos() + 3),
|
||||
//ctrl(alt(key(Down))) => SetNoteScroll(s.note_pos().saturating_sub(3)),
|
||||
//ctrl(alt(key(Left))) => SetTimeScroll(s.time_pos().saturating_sub(s.time_zoom().get())),
|
||||
//ctrl(alt(key(Right))) => SetTimeScroll((s.time_pos() + s.time_zoom().get()) % s.clip_length()),
|
||||
//ctrl(key(Up)) => SetNoteScroll(s.note_lo().get() + 1),
|
||||
//ctrl(key(Down)) => SetNoteScroll(s.note_lo().get().saturating_sub(1)),
|
||||
//ctrl(key(Left)) => SetTimeScroll(s.time_start().get().saturating_sub(s.note_len())),
|
||||
//ctrl(key(Right)) => SetTimeScroll(s.time_start().get() + s.note_len()),
|
||||
//alt(key(Up)) => SetNoteCursor(s.note_point() + 3),
|
||||
//alt(key(Down)) => SetNoteCursor(s.note_point().saturating_sub(3)),
|
||||
//alt(key(Left)) => SetTimeCursor(s.time_point().saturating_sub(s.time_zoom().get())),
|
||||
//alt(key(Right)) => SetTimeCursor((s.time_point() + s.time_zoom().get()) % s.clip_length()),
|
||||
//key(Char('d')) => SetTimeCursor((s.time_point() + s.note_len()) % s.clip_length()),
|
||||
//alt(key(Up)) => SetNoteCursor(s.note_pos() + 3),
|
||||
//alt(key(Down)) => SetNoteCursor(s.note_pos().saturating_sub(3)),
|
||||
//alt(key(Left)) => SetTimeCursor(s.time_pos().saturating_sub(s.time_zoom().get())),
|
||||
//alt(key(Right)) => SetTimeCursor((s.time_pos() + s.time_zoom().get()) % s.clip_length()),
|
||||
//key(Char('d')) => SetTimeCursor((s.time_pos() + s.note_len()) % s.clip_length()),
|
||||
//key(Char('z')) => SetTimeLock(!s.time_lock().get()),
|
||||
//key(Char('-')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::next(s.time_zoom().get()) }),
|
||||
//key(Char('_')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::next(s.time_zoom().get()) }),
|
||||
|
|
@ -276,22 +298,3 @@ handle!(TuiIn: |self: MidiEditor, input|{
|
|||
////// TODO: kpat!(Char('/')) => // toggle 3plet
|
||||
////// TODO: kpat!(Char('?')) => // toggle dotted
|
||||
//});
|
||||
impl Command<MidiEditor> for MidiEditCommand {
|
||||
fn execute (self, state: &mut MidiEditor) -> Perhaps<Self> {
|
||||
use MidiEditCommand::*;
|
||||
match self {
|
||||
Show(clip) => { state.set_clip(clip.as_ref()); },
|
||||
PutNote => { state.put_note(false); },
|
||||
AppendNote => { state.put_note(true); },
|
||||
SetTimeZoom(x) => { state.time_zoom().set(x); state.redraw(); },
|
||||
SetTimeLock(x) => { state.time_lock().set(x); },
|
||||
SetTimeScroll(x) => { state.time_start().set(x); },
|
||||
SetNoteScroll(x) => { state.note_lo().set(x.min(127)); },
|
||||
SetNoteLength(x) => { state.set_note_len(x); },
|
||||
SetTimeCursor(x) => { state.set_time_point(x); },
|
||||
SetNoteCursor(note) => { state.set_note_point(note.min(127)); },
|
||||
_ => todo!("{:?}", self)
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ use crate::*;
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct MidiPointModel {
|
||||
/// Time coordinate of cursor
|
||||
pub time_point: Arc<AtomicUsize>,
|
||||
pub time_pos: Arc<AtomicUsize>,
|
||||
/// Note coordinate of cursor
|
||||
pub note_point: Arc<AtomicUsize>,
|
||||
pub note_pos: Arc<AtomicUsize>,
|
||||
/// Length of note that will be inserted, in pulses
|
||||
pub note_len: Arc<AtomicUsize>,
|
||||
}
|
||||
|
|
@ -13,8 +13,8 @@ pub struct MidiPointModel {
|
|||
impl Default for MidiPointModel {
|
||||
fn default () -> Self {
|
||||
Self {
|
||||
time_point: Arc::new(0.into()),
|
||||
note_point: Arc::new(36.into()),
|
||||
time_pos: Arc::new(0.into()),
|
||||
note_pos: Arc::new(36.into()),
|
||||
note_len: Arc::new(24.into()),
|
||||
}
|
||||
}
|
||||
|
|
@ -23,14 +23,14 @@ impl Default for MidiPointModel {
|
|||
pub trait NotePoint {
|
||||
fn note_len (&self) -> usize;
|
||||
fn set_note_len (&self, x: usize);
|
||||
fn note_point (&self) -> usize;
|
||||
fn set_note_point (&self, x: usize);
|
||||
fn note_end (&self) -> usize { self.note_point() + self.note_len() }
|
||||
fn note_pos (&self) -> usize;
|
||||
fn set_note_pos (&self, x: usize);
|
||||
fn note_end (&self) -> usize { self.note_pos() + self.note_len() }
|
||||
}
|
||||
|
||||
pub trait TimePoint {
|
||||
fn time_point (&self) -> usize;
|
||||
fn set_time_point (&self, x: usize);
|
||||
fn time_pos (&self) -> usize;
|
||||
fn set_time_pos (&self, x: usize);
|
||||
}
|
||||
|
||||
pub trait MidiPoint: NotePoint + TimePoint {}
|
||||
|
|
@ -40,11 +40,11 @@ impl<T: NotePoint + TimePoint> MidiPoint for T {}
|
|||
impl NotePoint for MidiPointModel {
|
||||
fn note_len (&self) -> usize { self.note_len.load(Relaxed)}
|
||||
fn set_note_len (&self, x: usize) { self.note_len.store(x, Relaxed) }
|
||||
fn note_point (&self) -> usize { self.note_point.load(Relaxed).min(127) }
|
||||
fn set_note_point (&self, x: usize) { self.note_point.store(x.min(127), Relaxed) }
|
||||
fn note_pos (&self) -> usize { self.note_pos.load(Relaxed).min(127) }
|
||||
fn set_note_pos (&self, x: usize) { self.note_pos.store(x.min(127), Relaxed) }
|
||||
}
|
||||
|
||||
impl TimePoint for MidiPointModel {
|
||||
fn time_point (&self) -> usize { self.time_point.load(Relaxed) }
|
||||
fn set_time_point (&self, x: usize) { self.time_point.store(x, Relaxed) }
|
||||
fn time_pos (&self) -> usize { self.time_pos.load(Relaxed) }
|
||||
fn set_time_pos (&self, x: usize) { self.time_pos.store(x, Relaxed) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,13 +11,13 @@ pub trait MidiViewer: HasSize<TuiOut> + MidiRange + MidiPoint + Debug + Send + S
|
|||
}
|
||||
/// Make sure cursor is within note range
|
||||
fn autoscroll (&self) {
|
||||
let note_point = self.note_point().min(127);
|
||||
let note_pos = self.note_pos().min(127);
|
||||
let note_lo = self.note_lo().get();
|
||||
let note_hi = self.note_hi();
|
||||
if note_point < note_lo {
|
||||
self.note_lo().set(note_point);
|
||||
} else if note_point > note_hi {
|
||||
self.note_lo().set((note_lo + note_point).saturating_sub(note_hi));
|
||||
if note_pos < note_lo {
|
||||
self.note_lo().set(note_pos);
|
||||
} else if note_pos > note_hi {
|
||||
self.note_lo().set((note_lo + note_pos).saturating_sub(note_hi));
|
||||
}
|
||||
}
|
||||
/// Make sure time range is within display
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use crate::*;
|
||||
use super::*;
|
||||
/// A clip, rendered as a horizontal piano roll.
|
||||
pub struct PianoHorizontal {
|
||||
pub clip: Option<Arc<RwLock<MidiClip>>>,
|
||||
|
|
@ -83,7 +82,7 @@ impl PianoHorizontal {
|
|||
let style = Style::default().fg(clip.color.base.rgb);//.bg(Color::Rgb(0, 0, 0));
|
||||
let mut notes_on = [false;128];
|
||||
for (x, time_start) in (0..clip.length).step_by(zoom).enumerate() {
|
||||
for (y, note) in (0..=127).rev().enumerate() {
|
||||
for (_y, note) in (0..=127).rev().enumerate() {
|
||||
if let Some(cell) = buf.get_mut(x, note) {
|
||||
if notes_on[note] {
|
||||
cell.set_char('▂');
|
||||
|
|
@ -114,19 +113,17 @@ impl PianoHorizontal {
|
|||
}
|
||||
fn notes (&self) -> impl Content<TuiOut> {
|
||||
let time_start = self.time_start().get();
|
||||
let note_axis = self.note_axis().get();
|
||||
let note_lo = self.note_lo().get();
|
||||
let note_hi = self.note_hi();
|
||||
let note_point = self.note_point();
|
||||
let buffer = self.buffer.clone();
|
||||
RenderThunk::new(move|to: &mut TuiOut|{
|
||||
let source = buffer.read().unwrap();
|
||||
let [x0, y0, w, h] = to.area().xywh();
|
||||
let [x0, y0, w, _h] = to.area().xywh();
|
||||
//if h as usize != note_axis {
|
||||
//panic!("area height mismatch: {h} <> {note_axis}");
|
||||
//}
|
||||
for (area_x, screen_x) in (x0..x0+w).enumerate() {
|
||||
for (area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
||||
for (area_y, screen_y, _note) in note_y_iter(note_lo, note_hi, y0) {
|
||||
let source_x = time_start + area_x;
|
||||
let source_y = note_hi - area_y;
|
||||
// TODO: enable loop rollover:
|
||||
|
|
@ -150,19 +147,19 @@ impl PianoHorizontal {
|
|||
let note_hi = self.note_hi();
|
||||
let note_len = self.note_len();
|
||||
let note_lo = self.note_lo().get();
|
||||
let note_point = self.note_point();
|
||||
let time_point = self.time_point();
|
||||
let note_pos = self.note_pos();
|
||||
let time_pos = self.time_pos();
|
||||
let time_start = self.time_start().get();
|
||||
let time_zoom = self.time_zoom().get();
|
||||
RenderThunk::new(move|to: &mut TuiOut|{
|
||||
let [x0, y0, w, _] = to.area().xywh();
|
||||
for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
||||
if note == note_point {
|
||||
if note == note_pos {
|
||||
for x in 0..w {
|
||||
let screen_x = x0 + x;
|
||||
let time_1 = time_start + x as usize * time_zoom;
|
||||
let time_2 = time_1 + time_zoom;
|
||||
if time_1 <= time_point && time_point < time_2 {
|
||||
if time_1 <= time_pos && time_pos < time_2 {
|
||||
to.blit(&"█", screen_x, screen_y, style);
|
||||
let tail = note_len as u16 / time_zoom as u16;
|
||||
for x_tail in (screen_x + 1)..(screen_x + tail) {
|
||||
|
|
@ -181,18 +178,18 @@ impl PianoHorizontal {
|
|||
let color = state.color;
|
||||
let note_lo = state.note_lo().get();
|
||||
let note_hi = state.note_hi();
|
||||
let note_point = state.note_point();
|
||||
let note_pos = state.note_pos();
|
||||
let key_style = Some(Style::default().fg(Color::Rgb(192, 192, 192)).bg(Color::Rgb(0, 0, 0)));
|
||||
let off_style = Some(Style::default().fg(TuiTheme::g(160)));
|
||||
let on_style = Some(Style::default().fg(TuiTheme::g(255)).bg(color.base.rgb).bold());
|
||||
Fill::y(Fixed::x(self.keys_width, RenderThunk::new(move|to: &mut TuiOut|{
|
||||
let [x, y0, w, h] = to.area().xywh();
|
||||
for (area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
||||
let [x, y0, _w, _h] = to.area().xywh();
|
||||
for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
||||
to.blit(&to_key(note), x, screen_y, key_style);
|
||||
if note > 127 {
|
||||
continue
|
||||
}
|
||||
if note == note_point {
|
||||
if note == note_pos {
|
||||
to.blit(&format!("{:<5}", Note::pitch_to_name(note)), x, screen_y, on_style)
|
||||
} else {
|
||||
to.blit(&Note::pitch_to_name(note), x, screen_y, off_style)
|
||||
|
|
@ -202,7 +199,7 @@ impl PianoHorizontal {
|
|||
}
|
||||
fn timeline (&self) -> impl Content<TuiOut> + '_ {
|
||||
Fill::x(Fixed::y(1, RenderThunk::new(move|to: &mut TuiOut|{
|
||||
let [x, y, w, h] = to.area();
|
||||
let [x, y, w, _h] = to.area();
|
||||
let style = Some(Style::default().dim());
|
||||
let length = self.clip.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
|
||||
for (area_x, screen_x) in (0..w).map(|d|(d, d+x)) {
|
||||
|
|
@ -231,12 +228,12 @@ impl NoteRange for PianoHorizontal {
|
|||
impl NotePoint for PianoHorizontal {
|
||||
fn note_len (&self) -> usize { self.point.note_len() }
|
||||
fn set_note_len (&self, x: usize) { self.point.set_note_len(x) }
|
||||
fn note_point (&self) -> usize { self.point.note_point() }
|
||||
fn set_note_point (&self, x: usize) { self.point.set_note_point(x) }
|
||||
fn note_pos (&self) -> usize { self.point.note_pos() }
|
||||
fn set_note_pos (&self, x: usize) { self.point.set_note_pos(x) }
|
||||
}
|
||||
impl TimePoint for PianoHorizontal {
|
||||
fn time_point (&self) -> usize { self.point.time_point() }
|
||||
fn set_time_point (&self, x: usize) { self.point.set_time_point(x) }
|
||||
fn time_pos (&self) -> usize { self.point.time_pos() }
|
||||
fn set_time_pos (&self, x: usize) { self.point.set_time_pos(x) }
|
||||
}
|
||||
impl MidiViewer for PianoHorizontal {
|
||||
fn clip (&self) -> &Option<Arc<RwLock<MidiClip>>> {
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ command!(|self:FileBrowserCommand,state:SamplerTui|match self {
|
|||
//_ => match input {
|
||||
//// load sample
|
||||
//kpat!(Shift-Char('L')) => Self::Import(FileBrowserCommand::Begin),
|
||||
//kpat!(KeyCode::Up) => Self::Select(state.note_point().overflowing_add(1).0.min(127)),
|
||||
//kpat!(KeyCode::Down) => Self::Select(state.note_point().overflowing_sub(1).0.min(127)),
|
||||
//kpat!(KeyCode::Up) => Self::Select(state.note_pos().overflowing_add(1).0.min(127)),
|
||||
//kpat!(KeyCode::Down) => Self::Select(state.note_pos().overflowing_sub(1).0.min(127)),
|
||||
//_ => return None
|
||||
//}
|
||||
//});
|
||||
|
|
@ -58,8 +58,8 @@ command!(|self: SamplerTuiCommand, state: SamplerTui|match self {
|
|||
None
|
||||
},
|
||||
Self::Select(index) => {
|
||||
let old = state.note_point();
|
||||
state.set_note_point(index);
|
||||
let old = state.note_pos();
|
||||
state.set_note_pos(index);
|
||||
Some(Self::Select(old))
|
||||
},
|
||||
Self::Sample(cmd) => cmd.execute(&mut state.state)?.map(Self::Sample),
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ content!(TuiOut: |self: SamplerTui| {
|
|||
Fill::xy(SamplesTui {
|
||||
color: self.color,
|
||||
note_hi: self.note_hi(),
|
||||
note_pt: self.note_point(),
|
||||
note_pt: self.note_pos(),
|
||||
height: self.size.h(),
|
||||
}),
|
||||
))),
|
||||
|
|
@ -74,14 +74,14 @@ impl NoteRange for SamplerTui {
|
|||
impl NotePoint for SamplerTui {
|
||||
fn note_len (&self) -> usize {0/*TODO*/}
|
||||
fn set_note_len (&self, x: usize) {}
|
||||
fn note_point (&self) -> usize { self.note_pt.load(Relaxed) }
|
||||
fn set_note_point (&self, x: usize) { self.note_pt.store(x, Relaxed); }
|
||||
fn note_pos (&self) -> usize { self.note_pt.load(Relaxed) }
|
||||
fn set_note_pos (&self, x: usize) { self.note_pt.store(x, Relaxed); }
|
||||
}
|
||||
impl Sampler {
|
||||
const EMPTY: &[(f64, f64)] = &[(0., 0.), (1., 1.), (2., 2.), (0., 2.), (2., 0.)];
|
||||
pub fn list <'a> (&'a self, compact: bool, editor: &MidiEditor) -> impl Content<TuiOut> + 'a {
|
||||
let note_lo = editor.note_lo().load(Relaxed);
|
||||
let note_pt = editor.note_point();
|
||||
let note_pt = editor.note_pos();
|
||||
let note_hi = editor.note_hi();
|
||||
Outer(Style::default().fg(TuiTheme::g(96))).enclose(Map::new(move||(note_lo..=note_hi).rev(), move|note, i| {
|
||||
let offset = |a|Push::y(i as u16, Align::n(Fixed::y(1, Fill::x(a))));
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ has_clips!(|self: Tek|self.pool.as_ref().expect("no clip pool").clips);
|
|||
has_jack!(|self: Tek|&self.jack);
|
||||
has_sampler!(|self: Tek|{
|
||||
sampler = self.sampler;
|
||||
index = self.editor.as_ref().map(|e|e.note_point()).unwrap_or(0); });
|
||||
index = self.editor.as_ref().map(|e|e.note_pos()).unwrap_or(0); });
|
||||
has_editor!(|self: Tek|{
|
||||
editor = self.editor;
|
||||
editor_w = {
|
||||
|
|
@ -502,20 +502,23 @@ impl Tek {
|
|||
} else {
|
||||
("⏹ ".to_string(), TuiTheme::g(64), TuiTheme::g(32))
|
||||
};
|
||||
let selected = selected_scene == Some(s) && selected_track == Some(t);
|
||||
let active = editing && selected;
|
||||
let label = move||Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string())));
|
||||
let neighbor = selected_track == Some(t) && selected_scene.map(|s|s+1) == Some(s);
|
||||
map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Either::new(
|
||||
editing && selected_scene == Some(s) && selected_track == Some(t),
|
||||
active,
|
||||
Thunk::new(||self.editor()),
|
||||
Thunk::new(move||phat_sel_3(
|
||||
selected_track == Some(t) && selected_scene == Some(s),
|
||||
Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string()))),
|
||||
Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string()))),
|
||||
if selected_track == Some(t) && selected_scene.map(|s|s+1) == Some(s) {
|
||||
None
|
||||
} else {
|
||||
Some(bg.into())
|
||||
},
|
||||
bg.into(),
|
||||
bg.into(),
|
||||
Thunk::new(move||Bsp::a(
|
||||
When::new(selected, Fill::y(Align::n(button(" Tab ".into(), "edit".into())))),
|
||||
phat_sel_3(
|
||||
selected,
|
||||
label(),
|
||||
label(),
|
||||
if neighbor { None } else { Some(bg.into()) },
|
||||
bg.into(),
|
||||
bg.into(),
|
||||
)
|
||||
)),
|
||||
))))
|
||||
}))).boxed()
|
||||
|
|
@ -1108,14 +1111,14 @@ audio!(|self: Tek, client, scope|{
|
|||
match event {
|
||||
(time, Ok(LiveEvent::Midi {message, ..})) => match message {
|
||||
MidiMessage::NoteOn {ref key, ..} if let Some(editor) = self.editor.as_ref() => {
|
||||
editor.set_note_point(key.as_int() as usize);
|
||||
editor.set_note_pos(key.as_int() as usize);
|
||||
},
|
||||
MidiMessage::Controller {controller, value} if let (Some(editor), Some(sampler)) = (
|
||||
self.editor.as_ref(),
|
||||
self.sampler.as_ref(),
|
||||
) => {
|
||||
// TODO: give sampler its own cursor
|
||||
if let Some(sample) = &sampler.mapped[editor.note_point()] {
|
||||
if let Some(sample) = &sampler.mapped[editor.note_pos()] {
|
||||
sample.write().unwrap().handle_cc(*controller, *value)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue