mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
provide components to App
This commit is contained in:
parent
cff87657b9
commit
ed462cd0f6
5 changed files with 172 additions and 82 deletions
13
cli/tek.rs
13
cli/tek.rs
|
|
@ -76,8 +76,8 @@ pub fn main () -> Usually<()> {
|
|||
let left_tos = PortConnection::collect(&cli.left_to, empty, empty);
|
||||
let right_froms = PortConnection::collect(&cli.right_from, empty, empty);
|
||||
let right_tos = PortConnection::collect(&cli.right_to, empty, empty);
|
||||
let audio_froms = &[&left_froms, &right_froms];
|
||||
let audio_tos = &[&left_tos, &right_tos ];
|
||||
let audio_froms = &[left_froms.as_slice(), right_froms.as_slice()];
|
||||
let audio_tos = &[left_tos.as_slice(), right_tos.as_slice() ];
|
||||
let perf = PerfModel::default();
|
||||
let size = Measure::new();
|
||||
let default_clip = ||Arc::new(RwLock::new(MidiClip::new(
|
||||
|
|
@ -93,10 +93,7 @@ pub fn main () -> Usually<()> {
|
|||
}
|
||||
clock
|
||||
};
|
||||
let default_clock = |jack: &Arc<RwLock<JackConnection>>|{
|
||||
let clock = Clock::from(jack);
|
||||
default_bpm(clock)
|
||||
};
|
||||
let default_clock = |jack: &Arc<RwLock<JackConnection>>|default_bpm(Clock::from(jack));
|
||||
// TODO: enable sync master/follow
|
||||
//let sync_clock = |jack: &Arc<RwLock<JackConnection>>, app|{
|
||||
//if cli.sync_lead {
|
||||
|
|
@ -148,7 +145,7 @@ pub fn main () -> Usually<()> {
|
|||
App::groovebox(
|
||||
jack, (&clip).into(), (&clip).into(),
|
||||
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({
|
||||
|
|
@ -156,7 +153,7 @@ pub fn main () -> Usually<()> {
|
|||
jack,
|
||||
PoolModel::default(),
|
||||
MidiEditor::default(), &midi_froms, &midi_tos,
|
||||
default_sampler(jack)?, &audio_froms, &audio_tos,
|
||||
default_sampler(jack)?, audio_froms, audio_tos,
|
||||
scenes, tracks, track_width
|
||||
)
|
||||
}))?)?,
|
||||
|
|
|
|||
178
tek/src/app.rs
178
tek/src/app.rs
|
|
@ -1,17 +1,6 @@
|
|||
use crate::*;
|
||||
#[derive(Default)]
|
||||
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 edn: String,
|
||||
pub clock: Clock,
|
||||
|
|
@ -34,7 +23,6 @@ pub struct App {
|
|||
pub size: Measure<TuiOut>,
|
||||
pub perf: PerfModel,
|
||||
}
|
||||
impl EdnViewData<TuiOut> for &App {}
|
||||
impl App {
|
||||
pub fn sequencer (
|
||||
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())));
|
||||
|
||||
/// 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,
|
||||
audio!(|self: App, _client, _scope|Control::Continue);
|
||||
handle!(TuiIn: |self: App, input| Ok(None));
|
||||
impl EdnViewData<TuiOut> for &App {
|
||||
fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> {
|
||||
use EdnItem::*;
|
||||
let w = self.tracks_with_sizes().last().map(|x|x.3 as u16).unwrap_or(0);
|
||||
match item {
|
||||
Nil => Box::new(()),
|
||||
Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())),
|
||||
Sym(":editor") => (&self.editor).boxed(),
|
||||
Sym(":inputs") => self.input_row(w).boxed(),
|
||||
Sym(":outputs") => self.output_row(w).boxed(),
|
||||
Sym(":pool") => self.pool().boxed(),
|
||||
Sym(":sample") => self.sample().boxed(),
|
||||
Sym(":sampler") => self.sampler().boxed(),
|
||||
Sym(":scenes") => self.scene_row(w).boxed(),
|
||||
Sym(":status") => self.status(0).boxed(),
|
||||
Sym(":toolbar") => self.toolbar().boxed(),
|
||||
Sym(":tracks") => self.track_row(w).boxed(),
|
||||
_ => panic!("no content for {item:?}")
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
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 fn tracks_with_sizes (&self)
|
||||
-> impl Iterator<Item = (usize, &ArrangerTrack, usize, usize)>
|
||||
{
|
||||
tracks_with_sizes(self.tracks.iter(), match self.selected {
|
||||
ArrangerSelection::Track(t) if self.is_editing() => Some(t),
|
||||
ArrangerSelection::Clip(t, _) if self.is_editing() => Some(t),
|
||||
_ => None
|
||||
}, self.editor_w())
|
||||
}
|
||||
fn is_editing (&self) -> bool {
|
||||
self.editing.load(Relaxed)
|
||||
}
|
||||
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> (
|
||||
tracks: impl Iterator<Item=&'a ArrangerTrack>,
|
||||
active: Option<usize>,
|
||||
bigger: usize
|
||||
) -> impl Iterator<Item=(usize,&'a ArrangerTrack,usize,usize)> {
|
||||
let mut x = 0;
|
||||
tracks.enumerate().map(move |(index, track)|{
|
||||
let width = if Some(index) == active { bigger } else { track.width.max(8) };
|
||||
let data = (index, track, x, x + width);
|
||||
x += width;
|
||||
data
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,30 @@ mod arranger_track; pub use self::arranger_track::*;
|
|||
mod arranger_h;
|
||||
use ClockCommand::{Play, Pause};
|
||||
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)));
|
||||
impl EdnViewData<TuiOut> for &Arranger {
|
||||
fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> {
|
||||
use EdnItem::*;
|
||||
let scenes_w = self.sidebar_w();
|
||||
let tracks_w = self.tracks_with_sizes().last().unwrap().3 as u16;
|
||||
match item {
|
||||
Nil => Box::new(()),
|
||||
|
|
@ -77,19 +96,11 @@ impl Arranger {
|
|||
pub fn tracks_with_sizes (&self)
|
||||
-> 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::Clip(t, _) if self.is_editing() => Some(t),
|
||||
_ => None
|
||||
};
|
||||
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
|
||||
})
|
||||
}, self.editor_w())
|
||||
}
|
||||
|
||||
fn play_row (&self, tracks_w: u16) -> impl Content<TuiOut> + '_ {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,20 @@ use MidiEditCommand::*;
|
|||
use MidiPoolCommand::*;
|
||||
use KeyCode::{Char, Delete, Tab, Up, Down, Left, Right};
|
||||
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)));
|
||||
impl EdnViewData<TuiOut> for &Groovebox {
|
||||
fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,24 @@ use SequencerCommand as Cmd;
|
|||
use MidiEditCommand::*;
|
||||
use MidiPoolCommand::*;
|
||||
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 {
|
||||
fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> {
|
||||
use EdnItem::*;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue