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 origin
transport:
reset
cargo run --bin tek_transport
arranger:
reset
cargo run --bin tek_arranger
sequencer:
reset
cargo run --bin tek_sequencer
sequencer-release:
reset
cargo run --release --bin tek_sequencer
mixer:
reset
cargo run --bin tek_mixer
track:
reset
cargo run --bin tek_track
sampler:
reset
cargo run --bin tek_sampler
plugin:
reset
cargo run --bin tek_plugin

View file

@ -1,7 +1,6 @@
mod align; pub(crate) use align::*;
mod bsp; pub(crate) use bsp::*;
mod cond; pub(crate) use cond::*;
mod debug; pub(crate) use debug::*;
mod fill; pub(crate) use fill::*;
mod fixed; pub(crate) use fixed::*;
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::*;
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
#[derive(Default)]
pub struct Measure<E: Engine> {
@ -57,13 +67,12 @@ impl Render<Tui> for Measure<Tui> {
impl Measure<Tui> {
pub fn debug (&self) -> ShowMeasure {
let measure: Measure<Tui> = (*self).clone();
ShowMeasure(measure)
ShowMeasure(&self)
}
}
pub struct ShowMeasure(Measure<Tui>);
render!(|self: ShowMeasure|render(|to|Ok({
pub struct ShowMeasure<'a>(&'a Measure<Tui>);
render!(|self: ShowMeasure<'a>|render(|to|Ok({
let w = self.0.w();
let h = self.0.h();
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(
state.phrases.phrases[state.phrases.phrase.load(Ordering::Relaxed)].clone()
))),
// WSAD navigation, Q launches, E edits, PgUp/Down pool, Arrows editor
_ => match state.focused() {
ArrangerFocus::Transport(_) => {
match to_transport_command(state, input)? {

View file

@ -196,14 +196,14 @@ render!(|self: SequencerTui|lay!([self.size, Tui::split_up(false, 1,
col!([
Tui::fixed_y(2, TransportView::from((
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 {
true
} else {
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
#[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,
modifiers: KeyModifiers::CONTROL, kind: KeyEventKind::Press, state: KeyEventState::NONE
})) };
(Ctrl-$code:expr) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code,
modifiers: KeyModifiers::CONTROL, kind: KeyEventKind::Press, state: KeyEventState::NONE
})) };
(Alt-$code:pat) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code,
modifiers: KeyModifiers::ALT, kind: KeyEventKind::Press, state: KeyEventState::NONE
})) };
(Alt-$code:expr) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code,
modifiers: KeyModifiers::ALT, kind: KeyEventKind::Press, state: KeyEventState::NONE
})) };
(Shift-$code:pat) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code,
modifiers: KeyModifiers::SHIFT, kind: KeyEventKind::Press, state: KeyEventState::NONE
})) };

View file

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

View file

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