wip: what is up with the arranger after all

This commit is contained in:
🪞👃🪞 2025-01-06 22:24:39 +01:00
parent abc1cc8fce
commit 7ff731133c
5 changed files with 156 additions and 124 deletions

View file

@ -5,14 +5,14 @@ mod arranger_scene; pub(crate) use self::arranger_scene::*;
mod arranger_select; pub(crate) use self::arranger_select::*;
mod arranger_track; pub(crate) use self::arranger_track::*;
mod arranger_mode; pub(crate) use self::arranger_mode::*;
mod arranger_v; #[allow(unused)] pub(crate) use self::arranger_v::*;
mod arranger_h;
mod arranger_v_clips; pub(crate) use self::arranger_v_clips::*;
mod arranger_v_cursor; pub(crate) use self::arranger_v_cursor::*;
mod arranger_v_head; pub(crate) use self::arranger_v_head::*;
mod arranger_v_io; pub(crate) use self::arranger_v_io::*;
mod arranger_v_sep; pub(crate) use self::arranger_v_sep::*;
pub(crate) const HEADER_H: u16 = 5;
pub(crate) const SCENES_W_OFFSET: u16 = 3;
/// Root view for standalone `tek_arranger`
pub struct Arranger {
jack: Arc<RwLock<JackConnection>>,
@ -98,14 +98,14 @@ from_jack!(|jack| Arranger {
compact: false,
}
});
impl Arranger {
fn render_mode (state: &Self) -> impl Content<TuiOut> + use<'_> {
match state.mode {
ArrangerMode::H => todo!("horizontal arranger"),
ArrangerMode::V(factor) => Self::render_mode_v(state, factor),
}
}
}
//impl Arranger {
//fn render_mode (state: &Self) -> impl Content<TuiOut> + use<'_> {
//match state.mode {
//ArrangerMode::H => todo!("horizontal arranger"),
//ArrangerMode::V(factor) => Self::render_mode_v(state, factor),
//}
//}
//}
//render!(TuiOut: (self: Arranger) => {
//let pool_w = if self.pool.visible { self.splits[1] } else { 0 };
//let color = self.color;
@ -122,17 +122,38 @@ impl Arranger {
//});
render!(TuiOut: (self: Arranger) => self.size.of(
Bsp::s(self.toolbar_view(),
Bsp::n(self.selector_view(),
Bsp::n(self.status_view(),
Bsp::w(self.pool_view(), Fill::xy(&self.editor)))))));
Bsp::n(self.selector_view(),
Bsp::s(Align::nw(Fill::x(Fixed::y(3, self.header()))),
Bsp::s(Align::nw(Fill::x(Fixed::y(1, self.ins()))),
Bsp::n(Align::nw(Fill::x(Fixed::y(1, self.outs()))),
Bsp::w(self.pool_view(), Fill::xy(lay!(
Align::nw(Fill::xy(Tui::bg(self.color.darkest.rgb, " "))),
Align::nw(Fill::xy(ArrangerVColSep::from(self))),
Align::nw(Fill::xy(ArrangerVRowSep::from((self, 1)))),
Align::nw(Fill::xy(ArrangerVCursor::from((self, 1)))),
Align::nw(Fill::xy(":")))))))))))));
//"todo:"))))))));
//Bsp::s(
//Align::nw(Fixed::y(1, Fill::x(ArrangerVIns::from(self)))),
//Bsp::s(
//Fixed::y(20, Align::nw(ArrangerVClips::new(self, 1))),
//Fill::x(Fixed::y(1, ArrangerVOuts::from(self)))))))))))));
//Bsp::s(
//Bsp::s(
//Bsp::s(
//Fill::xy(ArrangerVClips::new(self, 1)),
//Fill::x(ArrangerVOuts::from(self)))))
impl Arranger {
fn toolbar_view (&self) -> impl Content<TuiOut> + use<'_> {
Fill::x(Fixed::y(2, Align::x(TransportView::new(true, &self.clock))))
}
fn status_view (&self) -> impl Content<TuiOut> + use<'_> {
let edit_clip = MidiEditClip(&self.editor);
//let selectors = When(false, Bsp::e(ClipSelected::play_phrase(&self.player), ClipSelected::next_phrase(&self.player)));
row!(/*selectors,*/ edit_clip, MidiEditStatus(&self.editor))
ArrangerStatus::from(self)
//let edit_clip = MidiEditClip(&self.editor);
////let selectors = When(false, Bsp::e(ClipSelected::play_phrase(&self.player), ClipSelected::next_phrase(&self.player)));
//row!([>selectors,<] edit_clip, MidiEditStatus(&self.editor))
}
fn selector_view (&self) -> impl Content<TuiOut> + use<'_> {
row!(
@ -149,6 +170,87 @@ impl Arranger {
let pool = Pull::y(1, Fill::y(Align::e(PoolView(self.pool.visible, &self.pool))));
Fixed::x(pool_w, Align::e(Fill::y(PoolView(self.compact, &self.pool))))
}
fn header (&self) -> impl Content<TuiOut> + use<'_> {
let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16;
fn row <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
row!(Tui::fg(color.light.rgb, ""), Tui::fg(color.lightest.rgb, field))
}
Push::x(scenes_w, Map(||ArrangerTrack::with_widths(self.tracks.as_slice()), |(_, track, x1, x2), i| {
let (w, h) = (ArrangerTrack::MIN_WIDTH.max(x2 - x1), HEADER_H);
let color = track.color();
Push::x(x1 as u16, Tui::bg(color.base.rgb, Min::xy(w as u16, h, Fixed::xy(w as u16, 5, col!(
row(color, Self::format_name(track, w)),
row(color, Self::format_elapsed(track, self.clock().timebase())),
row(color, Self::format_until_next(track, &self.clock().playhead)),
)))))
}))
}
/// name and width of track
fn format_name (track: &ArrangerTrack, _w: usize) -> impl Content<TuiOut> {
let name = track.name().read().unwrap().clone();
Tui::bold(true, Tui::fg(track.color.lightest.rgb, name))
}
/// beats elapsed
fn format_elapsed (track: &ArrangerTrack, timebase: &Arc<Timebase>) -> impl Content<TuiOut> {
let mut result = String::new();
if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() {
let length = phrase.read().unwrap().length;
let elapsed = track.player.pulses_since_start().unwrap();
let elapsed = timebase.format_beats_1_short(
(elapsed as usize % length) as f64
);
result = format!("+{elapsed:>}")
}
result
}
/// beats until switchover
fn format_until_next (track: &ArrangerTrack, current: &Arc<Moment>)
-> Option<impl Content<TuiOut>>
{
let timebase = &current.timebase;
let mut result = String::new();
if let Some((t, _)) = track.player.next_phrase().as_ref() {
let target = t.pulse.get();
let current = current.pulse.get();
if target > current {
let remaining = target - current;
result = format!("-{:>}", timebase.format_beats_0_short(remaining))
}
}
Some(result)
}
fn ins (&self) -> impl Content<TuiOut> + use<'_> {
let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16;
fn row <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
row!(Tui::fg(color.light.rgb, ""), Tui::fg(color.lightest.rgb, field))
}
Push::x(scenes_w, Map(||ArrangerTrack::with_widths(self.tracks.as_slice()), |(_, track, x1, x2), i| {
let (w, h) = (ArrangerTrack::MIN_WIDTH.max(x2 - x1), HEADER_H);
let color = track.color();
let input = Self::format_input(track);
Push::x(x1 as u16, Tui::bg(color.base.rgb, Min::xy(w as u16, h, Fixed::xy(w as u16, 5, row(color, Self::format_input(track).ok())))))
}))
}
fn format_input (track: &ArrangerTrack) -> Usually<impl Content<TuiOut>> {
Ok(format!(">{}", track.player.midi_ins().first().map(|port|port.short_name())
.transpose()?.unwrap_or("?".into())))
}
fn outs (&self) -> impl Content<TuiOut> + use<'_> {
let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16;
fn row <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
row!(Tui::fg(color.light.rgb, ""), Tui::fg(color.lightest.rgb, field))
}
Push::x(scenes_w, Map(||ArrangerTrack::with_widths(self.tracks.as_slice()), |(_, track, x1, x2), i| {
let (w, h) = (ArrangerTrack::MIN_WIDTH.max(x2 - x1), HEADER_H);
let color = track.color();
Push::x(x2 as u16, Tui::bg(color.base.rgb, Min::xy(w as u16, h, Fixed::xy(w as u16, 5, row(color, Self::format_output(track).ok())))))
}))
}
/// output port
fn format_output (track: &ArrangerTrack) -> Usually<impl Content<TuiOut>> {
Ok(format!("<{}", track.player.midi_outs().first().map(|port|port.short_name())
.transpose()?.unwrap_or("?".into())))
}
}
//render!(TuiOut: (self: Arranger) => {
//let pool_w = if self.pool.visible { self.splits[1] } else { 0 };

View file

@ -1,14 +1,12 @@
use crate::*;
// egyptian snakes den
pub(crate) const HEADER_H: u16 = 5;
pub(crate) const SCENES_W_OFFSET: u16 = 3;
impl Arranger {
pub fn render_mode_v (state: &Arranger, factor: usize) -> impl Content<TuiOut> + use<'_> {
lay!(
ArrangerVColSep::from(state),
ArrangerVRowSep::from((state, factor)),
col!(
ArrangerVHead::from(state),
//ArrangerVHead::from(state),
ArrangerVIns::from(state),
ArrangerVClips::new(state, factor),
ArrangerVOuts::from(state),

View file

@ -1,84 +1,41 @@
use crate::*;
use super::*;
pub struct ArrangerVHead<'a> {
pub struct ArrangerVIns<'a> {
size: &'a Measure<TuiOut>,
tracks: &'a Vec<ArrangerTrack>,
scenes_w: u16,
timebase: &'a Arc<Timebase>,
current: &'a Arc<Moment>,
tracks: &'a [ArrangerTrack],
}
from!(<'a>|state: &'a Arranger|ArrangerVHead<'a> = Self { // A
tracks: &state.tracks,
timebase: state.clock().timebase(),
current: &state.clock().playhead,
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&state.scenes) as u16,
});
render!(TuiOut: (self: ArrangerVHead<'a>) => {
fn row <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
row!(Tui::fg(color.light.rgb, ""), Tui::fg(color.lightest.rgb, field))
}
Some(Push::x(self.scenes_w,
Map(||ArrangerTrack::with_widths(self.tracks), |(_, track, x1, x2), i| {
let (w, h) = (ArrangerTrack::MIN_WIDTH.max(x2 - x1), HEADER_H);
let color = track.color();
let input = Self::format_input(track);
let output = Self::format_output(track);
Tui::bg(color.base.rgb, Min::xy(w as u16, h, Fixed::xy(w as u16, 5, col!(
row(color, Self::format_name(track, w)),
row(color, Self::format_input(track).ok()),
row(color, Self::format_output(track).ok()),
row(color, Self::format_elapsed(track, self.timebase)),
row(color, Self::format_until_next(track, self.current)),
))))
})
))
});
impl ArrangerVHead<'_> {
/// name and width of track
fn format_name (track: &ArrangerTrack, _w: usize) -> impl Content<TuiOut> {
let name = track.name().read().unwrap().clone();
Tui::bold(true, Tui::fg(track.color.lightest.rgb, name))
}
impl ArrangerVIns<'_> {
/// input port
fn format_input (track: &ArrangerTrack) -> Usually<impl Content<TuiOut>> {
Ok(format!(">{}", track.player.midi_ins().first().map(|port|port.short_name())
.transpose()?.unwrap_or("?".into())))
}
/// output port
fn format_output (track: &ArrangerTrack) -> Usually<impl Content<TuiOut>> {
Ok(format!("<{}", track.player.midi_outs().first().map(|port|port.short_name())
.transpose()?.unwrap_or("?".into())))
}
/// beats elapsed
fn format_elapsed (track: &ArrangerTrack, timebase: &Arc<Timebase>) -> impl Content<TuiOut> {
let mut result = String::new();
if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() {
let length = phrase.read().unwrap().length;
let elapsed = track.player.pulses_since_start().unwrap();
let elapsed = timebase.format_beats_1_short(
(elapsed as usize % length) as f64
);
result = format!("+{elapsed:>}")
}
result
}
/// beats until switchover
fn format_until_next (track: &ArrangerTrack, current: &Arc<Moment>)
-> Option<impl Content<TuiOut>>
{
let timebase = &current.timebase;
let mut result = String::new();
if let Some((t, _)) = track.player.next_phrase().as_ref() {
let target = t.pulse.get();
let current = current.pulse.get();
if target > current {
let remaining = target - current;
result = format!("-{:>}", timebase.format_beats_0_short(remaining))
}
}
Some(result)
}
}
from!(<'a>|args: &'a Arranger|ArrangerVIns<'a> = Self {
size: &args.size,
tracks: &args.tracks,
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&args.scenes) as u16,
});
render!(TuiOut: (self: ArrangerVIns<'a>) => {
});
pub struct ArrangerVOuts<'a> {
size: &'a Measure<TuiOut>,
tracks: &'a Vec<ArrangerTrack>,
scenes_w: u16,
}
impl ArrangerVOuts<'_> {
}
from!(<'a>|args: &'a Arranger|ArrangerVOuts<'a> = Self {
size: &args.size,
tracks: &args.tracks,
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&args.scenes) as u16,
});
render!(TuiOut: (self: ArrangerVOuts<'a>) => {
});

View file

@ -1,26 +0,0 @@
use crate::*;
use super::*;
pub struct ArrangerVIns<'a> {
size: &'a Measure<TuiOut>,
tracks: &'a Vec<ArrangerTrack>,
}
from!(<'a>|args: &'a Arranger|ArrangerVIns<'a> = Self {
size: &args.size,
tracks: &args.tracks,
});
render!(TuiOut: (self: ArrangerVIns<'a>) => "");
pub struct ArrangerVOuts<'a> {
size: &'a Measure<TuiOut>,
tracks: &'a Vec<ArrangerTrack>,
}
from!(<'a>|args: &'a Arranger|ArrangerVOuts<'a> = Self {
size: &args.size,
tracks: &args.tracks,
});
render!(TuiOut: (self: ArrangerVOuts<'a>) => "");

View file

@ -26,17 +26,18 @@ pub struct ArrangerVRowSep {
rows: Vec<(usize, usize)>,
}
from!(|args:(&Arranger, usize)|ArrangerVRowSep = Self {
fg: TuiTheme::separator_fg(false),
fg: Color::Rgb(255,255,255,),
rows: ArrangerScene::ppqs(&args.0.scenes, args.1),
});
render!(TuiOut: |self: ArrangerVRowSep, to|for y in self.rows.iter().map(|row|row.1) {
let y = to.area().y() + (y / PPQ) as u16 + 1;
if y >= to.buffer.area.height { break }
for x in to.area().x()..to.area().x2().saturating_sub(2) {
if x < to.buffer.area.x && y < to.buffer.area.y {
let cell = to.buffer.get_mut(x, y);
cell.modifier = Modifier::UNDERLINED;
cell.underline_color = self.fg;
}
//if x < to.buffer.area.x && y < to.buffer.area.y {
if let Some(cell) = to.buffer.cell_mut(ratatui::prelude::Position::from((x, y))) {
cell.modifier = Modifier::UNDERLINED;
cell.underline_color = self.fg;
}
//}
}
});