provide components to App

This commit is contained in:
🪞👃🪞 2025-01-11 20:48:29 +01:00
parent cff87657b9
commit ed462cd0f6
5 changed files with 172 additions and 82 deletions

View file

@ -76,8 +76,8 @@ pub fn main () -> Usually<()> {
let left_tos = PortConnection::collect(&cli.left_to, empty, empty); let left_tos = PortConnection::collect(&cli.left_to, empty, empty);
let right_froms = PortConnection::collect(&cli.right_from, empty, empty); let right_froms = PortConnection::collect(&cli.right_from, empty, empty);
let right_tos = PortConnection::collect(&cli.right_to, empty, empty); let right_tos = PortConnection::collect(&cli.right_to, empty, empty);
let audio_froms = &[&left_froms, &right_froms]; let audio_froms = &[left_froms.as_slice(), right_froms.as_slice()];
let audio_tos = &[&left_tos, &right_tos ]; let audio_tos = &[left_tos.as_slice(), right_tos.as_slice() ];
let perf = PerfModel::default(); let perf = PerfModel::default();
let size = Measure::new(); let size = Measure::new();
let default_clip = ||Arc::new(RwLock::new(MidiClip::new( let default_clip = ||Arc::new(RwLock::new(MidiClip::new(
@ -93,10 +93,7 @@ pub fn main () -> Usually<()> {
} }
clock clock
}; };
let default_clock = |jack: &Arc<RwLock<JackConnection>>|{ let default_clock = |jack: &Arc<RwLock<JackConnection>>|default_bpm(Clock::from(jack));
let clock = Clock::from(jack);
default_bpm(clock)
};
// TODO: enable sync master/follow // TODO: enable sync master/follow
//let sync_clock = |jack: &Arc<RwLock<JackConnection>>, app|{ //let sync_clock = |jack: &Arc<RwLock<JackConnection>>, app|{
//if cli.sync_lead { //if cli.sync_lead {
@ -148,7 +145,7 @@ pub fn main () -> Usually<()> {
App::groovebox( App::groovebox(
jack, (&clip).into(), (&clip).into(), jack, (&clip).into(), (&clip).into(),
Some(player), &midi_froms, &midi_tos, Some(player), &midi_froms, &midi_tos,
sampler, &audio_froms, &audio_tos, sampler, audio_froms, audio_tos,
) )
}))?)?, }))?)?,
TekMode::Arranger { scenes, tracks, track_width, .. } => engine.run(&jack.activate_with(|jack|Ok({ TekMode::Arranger { scenes, tracks, track_width, .. } => engine.run(&jack.activate_with(|jack|Ok({
@ -156,7 +153,7 @@ pub fn main () -> Usually<()> {
jack, jack,
PoolModel::default(), PoolModel::default(),
MidiEditor::default(), &midi_froms, &midi_tos, MidiEditor::default(), &midi_froms, &midi_tos,
default_sampler(jack)?, &audio_froms, &audio_tos, default_sampler(jack)?, audio_froms, audio_tos,
scenes, tracks, track_width scenes, tracks, track_width
) )
}))?)?, }))?)?,

View file

@ -1,17 +1,6 @@
use crate::*; use crate::*;
#[derive(Default)] #[derive(Default)]
pub struct App { pub struct App {
//jack: Arc<RwLock<JackConnection>>,
//view: EdnView<TuiOut, &'a Self>,
//pool: Option<PoolModel>,
//editor: Option<MidiEditor>,
//player: Option<MidiPlayer>,
//compact: AtomicBool,
//size: Measure<TuiOut>,
//perf: PerfModel,
//note_buf: Vec<u8>,
//midi_buf: Vec<Vec<Vec<u8>>>
pub jack: Arc<RwLock<JackConnection>>, pub jack: Arc<RwLock<JackConnection>>,
pub edn: String, pub edn: String,
pub clock: Clock, pub clock: Clock,
@ -34,7 +23,6 @@ pub struct App {
pub size: Measure<TuiOut>, pub size: Measure<TuiOut>,
pub perf: PerfModel, pub perf: PerfModel,
} }
impl EdnViewData<TuiOut> for &App {}
impl App { impl App {
pub fn sequencer ( pub fn sequencer (
jack: &Arc<RwLock<JackConnection>>, jack: &Arc<RwLock<JackConnection>>,
@ -100,58 +88,120 @@ impl App {
} }
} }
render!(TuiOut: (self: App) => self.size.of(EdnView::from_source(self, self.edn.as_ref()))); render!(TuiOut: (self: App) => self.size.of(EdnView::from_source(self, self.edn.as_ref())));
audio!(|self: App, _client, _scope|Control::Continue);
/// Root view for standalone `tek_sequencer`. handle!(TuiIn: |self: App, input| Ok(None));
pub struct Sequencer { impl EdnViewData<TuiOut> for &App {
pub _jack: Arc<RwLock<JackConnection>>, fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> {
use EdnItem::*;
pub pool: PoolModel, let w = self.tracks_with_sizes().last().map(|x|x.3 as u16).unwrap_or(0);
pub editor: MidiEditor, match item {
pub player: MidiPlayer, Nil => Box::new(()),
Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())),
pub transport: bool, Sym(":editor") => (&self.editor).boxed(),
pub selectors: bool, Sym(":inputs") => self.input_row(w).boxed(),
pub compact: bool, Sym(":outputs") => self.output_row(w).boxed(),
Sym(":pool") => self.pool().boxed(),
pub size: Measure<TuiOut>, Sym(":sample") => self.sample().boxed(),
pub status: bool, Sym(":sampler") => self.sampler().boxed(),
pub note_buf: Vec<u8>, Sym(":scenes") => self.scene_row(w).boxed(),
pub midi_buf: Vec<Vec<Vec<u8>>>, Sym(":status") => self.status(0).boxed(),
pub perf: PerfModel, Sym(":toolbar") => self.toolbar().boxed(),
Sym(":tracks") => self.track_row(w).boxed(),
_ => panic!("no content for {item:?}")
}
}
fn get_unit (&self, item: EdnItem<&str>) -> u16 {
use EdnItem::*;
match item.to_str() {
":sample-h" => if self.compact() { 0 } else { 5 },
":samples-w" => if self.compact() { 4 } else { 11 },
":samples-y" => if self.compact() { 1 } else { 0 },
":pool-w" => if self.compact() { 5 } else {
let w = self.size.w();
if w > 60 { 20 } else if w > 40 { 15 } else { 10 }
},
_ => 0
}
}
} }
impl App {
fn compact (&self) -> bool { false }
fn toolbar (&self) -> impl Content<TuiOut> + use<'_> {
Fill::x(Fixed::y(2, Align::x(TransportView::new(true, &self.clock))))
}
fn status (&self, note_pt: usize) -> impl Content<TuiOut> + use<'_> {
self.editor.as_ref()
.map(|e|Bsp::e(e.clip_status(), e.edit_status()))
}
fn pool (&self) -> impl Content<TuiOut> + use<'_> {
self.pool.as_ref()
.map(|pool|Align::e(Fixed::x(self.sidebar_w(), PoolView(self.compact(), pool))))
}
fn editor (&self) -> impl Content<TuiOut> + '_ {
&self.editor
}
fn sample <'a> (&'a self) -> impl Content<TuiOut> + 'a {
let compact = self.is_editing();
if let (Some(editor), Some(sampler)) = (&self.editor, &self.sampler) {
let note_pt = editor.note_point();
let sample_h = if compact { 0 } else { 5 };
return Some(Max::y(sample_h, Fill::xy(Bsp::a(
Fill::x(Align::w(Fixed::y(1, self.status(note_pt)))),
sampler.viewer(note_pt)
))))
}
None
}
fn sampler (&self) -> impl Content<TuiOut> + use<'_> {
let compact = self.is_editing();
if let (Some(editor), Some(sampler)) = (&self.editor, &self.sampler) {
let note_pt = editor.note_point();
let w = if compact { 4 } else { 40 };
let y = if compact { 1 } else { 0 };
return Some(Fixed::x(w, Push::y(y, Fill::y(sampler.list(compact, editor)))))
}
None
}
fn track_row (&self, w: u16) -> impl Content<TuiOut> + '_ { "" }
fn input_row (&self, w: u16) -> impl Content<TuiOut> + '_ { "" }
fn scene_row (&self, w: u16) -> impl Content<TuiOut> + '_ { "" }
fn output_row (&self, w: u16) -> impl Content<TuiOut> + '_ { "" }
pub struct Groovebox { pub fn tracks_with_sizes (&self)
pub _jack: Arc<RwLock<JackConnection>>, -> impl Iterator<Item = (usize, &ArrangerTrack, usize, usize)>
pub player: MidiPlayer, {
pub pool: PoolModel, tracks_with_sizes(self.tracks.iter(), match self.selected {
pub editor: MidiEditor, ArrangerSelection::Track(t) if self.is_editing() => Some(t),
pub sampler: Sampler, ArrangerSelection::Clip(t, _) if self.is_editing() => Some(t),
_ => None
pub compact: bool, }, self.editor_w())
pub size: Measure<TuiOut>, }
pub status: bool, fn is_editing (&self) -> bool {
pub note_buf: Vec<u8>, self.editing.load(Relaxed)
pub midi_buf: Vec<Vec<Vec<u8>>>, }
pub perf: PerfModel, fn editor_w (&self) -> usize {
let editor = self.editor.as_ref().expect("missing editor");
(5 + (editor.time_len().get() / editor.time_zoom().get()))
.min(self.size.w().saturating_sub(20))
.max(16)
}
fn sidebar_w (&self) -> u16 {
let w = self.size.w();
let w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
let w = if self.is_editing() { 8 } else { w };
w
}
} }
pub fn tracks_with_sizes <'a> (
/// Root view for standalone `tek_arranger` tracks: impl Iterator<Item=&'a ArrangerTrack>,
pub struct Arranger { active: Option<usize>,
pub jack: Arc<RwLock<JackConnection>>, bigger: usize
pub midi_ins: Vec<JackPort<MidiIn>>, ) -> impl Iterator<Item=(usize,&'a ArrangerTrack,usize,usize)> {
pub midi_outs: Vec<JackPort<MidiOut>>, let mut x = 0;
pub clock: Clock, tracks.enumerate().map(move |(index, track)|{
pub pool: PoolModel, let width = if Some(index) == active { bigger } else { track.width.max(8) };
pub tracks: Vec<ArrangerTrack>, let data = (index, track, x, x + width);
pub scenes: Vec<ArrangerScene>, x += width;
pub splits: [u16;2], data
pub selected: ArrangerSelection, })
pub color: ItemPalette,
pub size: Measure<TuiOut>,
pub note_buf: Vec<u8>,
pub midi_buf: Vec<Vec<Vec<u8>>>,
pub editor: MidiEditor,
pub editing: AtomicBool,
pub perf: PerfModel,
pub compact: bool,
} }

View file

@ -5,11 +5,30 @@ mod arranger_track; pub use self::arranger_track::*;
mod arranger_h; mod arranger_h;
use ClockCommand::{Play, Pause}; use ClockCommand::{Play, Pause};
use self::ArrangerCommand as Cmd; use self::ArrangerCommand as Cmd;
/// Root view for standalone `tek_arranger`
pub struct Arranger {
pub jack: Arc<RwLock<JackConnection>>,
pub midi_ins: Vec<JackPort<MidiIn>>,
pub midi_outs: Vec<JackPort<MidiOut>>,
pub clock: Clock,
pub pool: PoolModel,
pub tracks: Vec<ArrangerTrack>,
pub scenes: Vec<ArrangerScene>,
pub splits: [u16;2],
pub selected: ArrangerSelection,
pub color: ItemPalette,
pub size: Measure<TuiOut>,
pub note_buf: Vec<u8>,
pub midi_buf: Vec<Vec<Vec<u8>>>,
pub editor: MidiEditor,
pub editing: AtomicBool,
pub perf: PerfModel,
pub compact: bool,
}
render!(TuiOut: (self: Arranger) => self.size.of(EdnView::from_source(self, Self::EDN))); render!(TuiOut: (self: Arranger) => self.size.of(EdnView::from_source(self, Self::EDN)));
impl EdnViewData<TuiOut> for &Arranger { impl EdnViewData<TuiOut> for &Arranger {
fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> { fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> {
use EdnItem::*; use EdnItem::*;
let scenes_w = self.sidebar_w();
let tracks_w = self.tracks_with_sizes().last().unwrap().3 as u16; let tracks_w = self.tracks_with_sizes().last().unwrap().3 as u16;
match item { match item {
Nil => Box::new(()), Nil => Box::new(()),
@ -77,19 +96,11 @@ impl Arranger {
pub fn tracks_with_sizes (&self) pub fn tracks_with_sizes (&self)
-> impl Iterator<Item = (usize, &ArrangerTrack, usize, usize)> -> impl Iterator<Item = (usize, &ArrangerTrack, usize, usize)>
{ {
let active = match self.selected { tracks_with_sizes(self.tracks.iter(), match self.selected {
ArrangerSelection::Track(t) if self.is_editing() => Some(t), ArrangerSelection::Track(t) if self.is_editing() => Some(t),
ArrangerSelection::Clip(t, _) if self.is_editing() => Some(t), ArrangerSelection::Clip(t, _) if self.is_editing() => Some(t),
_ => None _ => None
}; }, self.editor_w())
let big = self.editor_w();
let mut x = 0;
self.tracks.iter().enumerate().map(move |(index, track)|{
let width = if Some(index) == active { big } else { track.width.max(8) };
let data = (index, track, x, x + width);
x += width;
data
})
} }
fn play_row (&self, tracks_w: u16) -> impl Content<TuiOut> + '_ { fn play_row (&self, tracks_w: u16) -> impl Content<TuiOut> + '_ {

View file

@ -7,6 +7,20 @@ use MidiEditCommand::*;
use MidiPoolCommand::*; use MidiPoolCommand::*;
use KeyCode::{Char, Delete, Tab, Up, Down, Left, Right}; use KeyCode::{Char, Delete, Tab, Up, Down, Left, Right};
use std::marker::ConstParamTy; use std::marker::ConstParamTy;
pub struct Groovebox {
pub _jack: Arc<RwLock<JackConnection>>,
pub player: MidiPlayer,
pub pool: PoolModel,
pub editor: MidiEditor,
pub sampler: Sampler,
pub compact: bool,
pub size: Measure<TuiOut>,
pub status: bool,
pub note_buf: Vec<u8>,
pub midi_buf: Vec<Vec<Vec<u8>>>,
pub perf: PerfModel,
}
render!(TuiOut: (self: Groovebox) => self.size.of(EdnView::from_source(self, Self::EDN))); render!(TuiOut: (self: Groovebox) => self.size.of(EdnView::from_source(self, Self::EDN)));
impl EdnViewData<TuiOut> for &Groovebox { impl EdnViewData<TuiOut> for &Groovebox {
fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> { fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> {

View file

@ -5,6 +5,24 @@ use SequencerCommand as Cmd;
use MidiEditCommand::*; use MidiEditCommand::*;
use MidiPoolCommand::*; use MidiPoolCommand::*;
render!(TuiOut: (self: Sequencer) => self.size.of(EdnView::from_source(self, Self::EDN))); render!(TuiOut: (self: Sequencer) => self.size.of(EdnView::from_source(self, Self::EDN)));
/// Root view for standalone `tek_sequencer`.
pub struct Sequencer {
pub _jack: Arc<RwLock<JackConnection>>,
pub pool: PoolModel,
pub editor: MidiEditor,
pub player: MidiPlayer,
pub transport: bool,
pub selectors: bool,
pub compact: bool,
pub size: Measure<TuiOut>,
pub status: bool,
pub note_buf: Vec<u8>,
pub midi_buf: Vec<Vec<Vec<u8>>>,
pub perf: PerfModel,
}
impl EdnViewData<TuiOut> for &Sequencer { impl EdnViewData<TuiOut> for &Sequencer {
fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> { fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> {
use EdnItem::*; use EdnItem::*;