wip: fix phrase editor

This commit is contained in:
🪞👃🪞 2024-12-14 15:24:26 +01:00
parent e92677d50c
commit 3995ec0f03
9 changed files with 131 additions and 95 deletions

View file

@ -17,18 +17,26 @@ ftpush:
git push --tags -fu codeberg git push --tags -fu codeberg
git push --tags -fu origin git push --tags -fu origin
transport: transport:
reset
cargo run --bin tek_transport cargo run --bin tek_transport
arranger: arranger:
reset
cargo run --bin tek_arranger cargo run --bin tek_arranger
sequencer: sequencer:
reset
cargo run --bin tek_sequencer cargo run --bin tek_sequencer
sequencer-release: sequencer-release:
reset
cargo run --release --bin tek_sequencer cargo run --release --bin tek_sequencer
mixer: mixer:
reset
cargo run --bin tek_mixer cargo run --bin tek_mixer
track: track:
reset
cargo run --bin tek_track cargo run --bin tek_track
sampler: sampler:
reset
cargo run --bin tek_sampler cargo run --bin tek_sampler
plugin: plugin:
reset
cargo run --bin tek_plugin cargo run --bin tek_plugin

View file

@ -1,7 +1,6 @@
mod align; pub(crate) use align::*; mod align; pub(crate) use align::*;
mod bsp; pub(crate) use bsp::*; mod bsp; pub(crate) use bsp::*;
mod cond; pub(crate) use cond::*; mod cond; pub(crate) use cond::*;
mod debug; pub(crate) use debug::*;
mod fill; pub(crate) use fill::*; mod fill; pub(crate) use fill::*;
mod fixed; pub(crate) use fixed::*; mod fixed; pub(crate) use fixed::*;
mod inset_outset; pub(crate) use inset_outset::*; mod inset_outset; pub(crate) use inset_outset::*;

View file

@ -1,11 +0,0 @@
use crate::*;
impl<E: Engine, W: Render<E>> LayoutDebug<E> for W {}
pub trait LayoutDebug<E: Engine>: Render<E> + Sized {
fn debug (self) -> DebugOverlay<E, Self> {
DebugOverlay(Default::default(), self)
}
}
pub struct DebugOverlay<E: Engine, W: Render<E>>(PhantomData<E>, pub W);

View file

@ -1,5 +1,15 @@
use crate::*; use crate::*;
impl<E: Engine> LayoutDebug<E> for E {}
pub trait LayoutDebug<E: Engine> {
fn debug <W: Render<E>> (other: W) -> DebugOverlay<E, W> {
DebugOverlay(Default::default(), other)
}
}
pub struct DebugOverlay<E: Engine, W: Render<E>>(PhantomData<E>, pub W);
/// A widget that tracks its render width and height /// A widget that tracks its render width and height
#[derive(Default)] #[derive(Default)]
pub struct Measure<E: Engine> { pub struct Measure<E: Engine> {
@ -57,13 +67,12 @@ impl Render<Tui> for Measure<Tui> {
impl Measure<Tui> { impl Measure<Tui> {
pub fn debug (&self) -> ShowMeasure { pub fn debug (&self) -> ShowMeasure {
let measure: Measure<Tui> = (*self).clone(); ShowMeasure(&self)
ShowMeasure(measure)
} }
} }
pub struct ShowMeasure(Measure<Tui>); pub struct ShowMeasure<'a>(&'a Measure<Tui>);
render!(|self: ShowMeasure|render(|to|Ok({ render!(|self: ShowMeasure<'a>|render(|to|Ok({
let w = self.0.w(); let w = self.0.w();
let h = self.0.h(); let h = self.0.h();
to.blit(&format!(" {w} x {h} "), to.area.x(), to.area.y(), Some( to.blit(&format!(" {w} x {h} "), to.area.x(), to.area.y(), Some(

View file

@ -1228,6 +1228,7 @@ fn to_arranger_command (state: &ArrangerTui, input: &TuiInput) -> Option<Arrange
key!(Char('e')) => Cmd::Editor(PhraseCommand::Show(Some( key!(Char('e')) => Cmd::Editor(PhraseCommand::Show(Some(
state.phrases.phrases[state.phrases.phrase.load(Ordering::Relaxed)].clone() state.phrases.phrases[state.phrases.phrase.load(Ordering::Relaxed)].clone()
))), ))),
// WSAD navigation, Q launches, E edits, PgUp/Down pool, Arrows editor
_ => match state.focused() { _ => match state.focused() {
ArrangerFocus::Transport(_) => { ArrangerFocus::Transport(_) => {
match to_transport_command(state, input)? { match to_transport_command(state, input)? {

View file

@ -196,14 +196,14 @@ render!(|self: SequencerTui|lay!([self.size, Tui::split_up(false, 1,
col!([ col!([
Tui::fixed_y(2, TransportView::from(( Tui::fixed_y(2, TransportView::from((
self, self,
self.player.play_phrase().as_ref().map(|(_,p)|p.as_ref().map(|p|p.read().unwrap().color)).flatten(), self.player.play_phrase().as_ref().map(|(_,p)|p.as_ref().map(|p|p.read().unwrap().color)).flatten().clone(),
if let SequencerFocus::Transport(_) = self.focus { if let SequencerFocus::Transport(_) = self.focus {
true true
} else { } else {
false false
} }
))), ))),
//self.editor Tui::fill_xy(&self.editor)
]), ]),
) )
)])); )]));

View file

@ -28,20 +28,39 @@ impl Input<Tui> for TuiInput {
} }
} }
//#[macro_export] macro_rules! key_pat {
//}
//#[macro_export] macro_rules! key_expr {
//}
/// Define key pattern in key match statement /// Define key pattern in key match statement
#[macro_export] macro_rules! key { #[macro_export] macro_rules! key {
(Ctrl-Alt-$code:pat) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code,
modifiers: KeyModifiers::CONTROL | KeyModifiers::ALT,
kind: KeyEventKind::Press,
state: KeyEventState::NONE
})) };
(Ctrl-Alt-$code:expr) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code,
modifiers: KeyModifiers::CONTROL | KeyModifiers::ALT,
kind: KeyEventKind::Press,
state: KeyEventState::NONE
})) };
(Ctrl-$code:pat) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, (Ctrl-$code:pat) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code,
modifiers: KeyModifiers::CONTROL, kind: KeyEventKind::Press, state: KeyEventState::NONE modifiers: KeyModifiers::CONTROL, kind: KeyEventKind::Press, state: KeyEventState::NONE
})) }; })) };
(Ctrl-$code:expr) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, (Ctrl-$code:expr) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code,
modifiers: KeyModifiers::CONTROL, kind: KeyEventKind::Press, state: KeyEventState::NONE modifiers: KeyModifiers::CONTROL, kind: KeyEventKind::Press, state: KeyEventState::NONE
})) }; })) };
(Alt-$code:pat) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, (Alt-$code:pat) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code,
modifiers: KeyModifiers::ALT, kind: KeyEventKind::Press, state: KeyEventState::NONE modifiers: KeyModifiers::ALT, kind: KeyEventKind::Press, state: KeyEventState::NONE
})) }; })) };
(Alt-$code:expr) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, (Alt-$code:expr) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code,
modifiers: KeyModifiers::ALT, kind: KeyEventKind::Press, state: KeyEventState::NONE modifiers: KeyModifiers::ALT, kind: KeyEventKind::Press, state: KeyEventState::NONE
})) }; })) };
(Shift-$code:pat) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, (Shift-$code:pat) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code,
modifiers: KeyModifiers::SHIFT, kind: KeyEventKind::Press, state: KeyEventState::NONE modifiers: KeyModifiers::SHIFT, kind: KeyEventKind::Press, state: KeyEventState::NONE
})) }; })) };

View file

@ -56,19 +56,21 @@ impl InputToCommand<Tui, PhraseEditorModel> for PhraseCommand {
_ => match from.event() { _ => match from.event() {
key!(Up) => SetNoteCursor(note_point + 1), key!(Up) => SetNoteCursor(note_point + 1),
key!(Down) => SetNoteCursor(note_point.saturating_sub(1)), key!(Down) => SetNoteCursor(note_point.saturating_sub(1)),
key!(PageUp) => SetNoteCursor(note_point + 3),
key!(PageDown) => SetNoteCursor(note_point.saturating_sub(3)),
key!(Left) => SetTimeCursor(time_point.saturating_sub(note_len)), key!(Left) => SetTimeCursor(time_point.saturating_sub(note_len)),
key!(Right) => SetTimeCursor((time_point + note_len) % length), key!(Right) => SetTimeCursor((time_point + note_len) % length),
key!(Shift-Left) => SetTimeCursor(time_point.saturating_sub(time_zoom)), key!(Alt-Up) => SetNoteCursor(note_point + 3),
key!(Shift-Right) => SetTimeCursor((time_point + time_zoom) % length), key!(Alt-Down) => SetNoteCursor(note_point.saturating_sub(3)),
key!(Alt-Left) => SetTimeCursor(time_point.saturating_sub(time_zoom)),
key!(Alt-Right) => SetTimeCursor((time_point + time_zoom) % length),
key!(Ctrl-Up) => SetNoteScroll(note_lo + 1), key!(Ctrl-Up) => SetNoteScroll(note_lo + 1),
key!(Ctrl-Down) => SetNoteScroll(note_lo.saturating_sub(1)), key!(Ctrl-Down) => SetNoteScroll(note_lo.saturating_sub(1)),
key!(Ctrl-PageUp) => SetNoteScroll(note_point + 3),
key!(Ctrl-PageDown) => SetNoteScroll(note_point.saturating_sub(3)),
key!(Ctrl-Left) => SetTimeScroll(time_start.saturating_sub(note_len)), key!(Ctrl-Left) => SetTimeScroll(time_start.saturating_sub(note_len)),
key!(Ctrl-Right) => SetTimeScroll(time_start + note_len), key!(Ctrl-Right) => SetTimeScroll(time_start + note_len),
key!(Ctrl-Alt-Up) => SetNoteScroll(note_point + 3),
key!(Ctrl-Alt-Down) => SetNoteScroll(note_point.saturating_sub(3)),
key!(Ctrl-Alt-Left) => SetTimeScroll(time_point.saturating_sub(time_zoom)),
key!(Ctrl-Alt-Right) => SetTimeScroll((time_point + time_zoom) % length),
_ => return None _ => return None
}, },
}) })
@ -151,6 +153,9 @@ impl PhraseViewMode for PhraseEditorModel {
fn phrase (&self) -> &Arc<RwLock<Option<Arc<RwLock<Phrase>>>>> { fn phrase (&self) -> &Arc<RwLock<Option<Arc<RwLock<Phrase>>>>> {
self.mode.phrase() self.mode.phrase()
} }
fn set_phrase (&mut self, phrase: Option<Arc<RwLock<Phrase>>>) {
self.mode.set_phrase(phrase)
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -194,12 +199,24 @@ impl PhraseEditorRange {
pub fn set_time_lock (&self, x: bool) { pub fn set_time_lock (&self, x: bool) {
self.time_lock.store(x, Relaxed); self.time_lock.store(x, Relaxed);
} }
pub fn time_start (&self) -> usize {
self.time_start.load(Relaxed)
}
pub fn set_time_start (&self, x: usize) { pub fn set_time_start (&self, x: usize) {
self.time_start.store(x, Relaxed); self.time_start.store(x, Relaxed);
} }
pub fn set_note_lo (&self, x: usize) { pub fn set_note_lo (&self, x: usize) {
self.note_lo.store(x, Relaxed); self.note_lo.store(x, Relaxed);
} }
pub fn note_lo (&self) -> usize {
self.note_lo.load(Relaxed)
}
pub fn note_axis (&self) -> usize {
self.note_lo.load(Relaxed)
}
pub fn note_hi (&self) -> usize {
self.note_lo() + self.note_axis()
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -228,6 +245,9 @@ impl PhraseEditorPoint {
pub fn set_note_len (&self, x: usize) { pub fn set_note_len (&self, x: usize) {
self.note_len.store(x, Relaxed) self.note_len.store(x, Relaxed)
} }
pub fn note_point (&self) -> usize {
self.note_point.load(Relaxed)
}
pub fn time_point (&self) -> usize { pub fn time_point (&self) -> usize {
self.time_point.load(Relaxed) self.time_point.load(Relaxed)
} }

View file

@ -4,6 +4,7 @@ use super::*;
/// A phrase, rendered as a horizontal piano roll. /// A phrase, rendered as a horizontal piano roll.
pub struct PianoHorizontal { pub struct PianoHorizontal {
phrase: Arc<RwLock<Option<Arc<RwLock<Phrase>>>>>, phrase: Arc<RwLock<Option<Arc<RwLock<Phrase>>>>>,
/// Buffer where the whole phrase is rerendered on change
buffer: BigBuffer, buffer: BigBuffer,
/// Width and height of notes area at last render /// Width and height of notes area at last render
size: Measure<Tui>, size: Measure<Tui>,
@ -11,6 +12,8 @@ pub struct PianoHorizontal {
range: PhraseEditorRange, range: PhraseEditorRange,
/// The note cursor /// The note cursor
point: PhraseEditorPoint, point: PhraseEditorPoint,
/// The highlight color palette
color: ItemPalette,
} }
impl PianoHorizontal { impl PianoHorizontal {
@ -19,56 +22,69 @@ impl PianoHorizontal {
let mut range = PhraseEditorRange::default(); let mut range = PhraseEditorRange::default();
range.time_axis = size.x.clone(); range.time_axis = size.x.clone();
range.note_axis = size.y.clone(); range.note_axis = size.y.clone();
let phrase = phrase.clone();
let color = phrase.read().unwrap().as_ref()
.map(|p|p.read().unwrap().color)
.unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64))));
Self { Self {
buffer: Default::default(), buffer: Default::default(),
phrase: phrase.clone(),
point: PhraseEditorPoint::default(), point: PhraseEditorPoint::default(),
range,
size, size,
range,
phrase,
color
} }
} }
} }
render!(|self: PianoHorizontal|{ render!(|self: PianoHorizontal|{
let bg = TuiTheme::g(32); let bg = TuiTheme::g(32);
let fg = self.phrase().read().unwrap() let fg = self.color;
.as_ref().map(|p|p.read().unwrap().color) let note_lo = self.range.note_lo();
.unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64)))); let note_hi = self.range.note_hi();
let time_lock = self.range.time_lock();
let time_start = self.range.time_start();
let time_zoom = self.range.time_zoom();
let time_point = self.point.time_point();
let note_point = self.point.note_point();
let note_len = self.point.note_len();
Tui::bg(bg, Tui::split_down(false, 1, Tui::bg(bg, Tui::split_down(false, 1,
Tui::bg(fg.dark.rgb, PianoHorizontalTimeline { Tui::debug(Tui::fill_x(Tui::push_x(5, Tui::bg(fg.darkest.rgb, Tui::fg(fg.lightest.rgb,
start: "|0".into() PianoHorizontalTimeline {
}), time_start,
Split::right(false, 5, PianoHorizontalKeys { time_zoom,
color: ItemPalette::random(), }
note_lo: 0, ))))),
note_hi: 0, Tui::fill_xy(Split::right(true, 5, Tui::debug(lay!([
note_point: None self.size,
}, lay!([
//self.size,
PianoHorizontalNotes { PianoHorizontalNotes {
source: &self.buffer, source: &self.buffer,
time_start: 0, time_start,
note_hi: 0, note_hi,
}, },
PianoHorizontalCursor { PianoHorizontalCursor {
time_zoom: 0, time_zoom,
time_point: 0, time_point,
time_start: 0, time_start,
note_point: 0, note_point: note_point,
note_len: 0, note_len: note_len,
note_hi: 0, note_hi: note_hi,
note_lo: 0, note_lo: note_lo,
}, },
])), ])), PianoHorizontalKeys {
color: self.color,
note_lo,
note_hi,
note_point: Some(note_point),
}))
)) ))
}); });
pub struct PianoHorizontalTimeline { pub struct PianoHorizontalTimeline {
start: String time_start: usize,
time_zoom: usize,
} }
render!(|self: PianoHorizontalTimeline|{ render!(|self: PianoHorizontalTimeline|format!("{}*{}", self.time_start, self.time_zoom).as_str());
Tui::fg(TuiTheme::g(224), Tui::push_x(5, self.start.as_str()))
});
pub struct PianoHorizontalKeys { pub struct PianoHorizontalKeys {
color: ItemPalette, color: ItemPalette,
@ -253,6 +269,13 @@ impl PhraseViewMode for PianoHorizontal {
}; };
self.buffer = buffer self.buffer = buffer
} }
fn set_phrase (&mut self, phrase: Option<Arc<RwLock<Phrase>>>) {
*self.phrase().write().unwrap() = phrase;
self.color = self.phrase.read().unwrap().as_ref()
.map(|p|p.read().unwrap().color)
.unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64))));
self.redraw();
}
} }
impl std::fmt::Debug for PianoHorizontal { impl std::fmt::Debug for PianoHorizontal {
@ -263,38 +286,6 @@ impl std::fmt::Debug for PianoHorizontal {
.finish() .finish()
} }
} }
//fn render_cursor (
//&self,
//to: &mut TuiOutput,
//time_point: usize,
//time_start: usize,
//note_point: usize,
//note_len: usize,
//note_hi: usize,
//note_lo: usize,
//) {
//let time_zoom = self.time_zoom;
//let [x0, y0, w, h] = to.area().xywh();
//let style = Some(Style::default().fg(Color::Rgb(0,255,0)));
//for (y, note) in (note_lo..=note_hi).rev().enumerate() {
//if note == note_point {
//for x in 0..w {
//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 {
//to.blit(&"█", x0 + x as u16, y0 + y as u16, style);
//let tail = note_len as u16 / time_zoom as u16;
//for x_tail in (x0 + x + 1)..(x0 + x + tail) {
//to.blit(&"▂", x_tail, y0 + y as u16, style);
//}
//break
//}
//}
//break
//}
//}
//}
// Update sequencer playhead indicator // Update sequencer playhead indicator
//self.now().set(0.); //self.now().set(0.);
//if let Some((ref started_at, Some(ref playing))) = self.player.play_phrase { //if let Some((ref started_at, Some(ref playing))) = self.player.play_phrase {